जावास्क्रिप्ट को सभी आधुनिक ब्राउज़र कार्यान्वयन में एकल-थ्रेडेड के रूप में जाना जाता है, लेकिन क्या यह किसी भी मानक में निर्दिष्ट है या यह सिर्फ परंपरा से है? क्या यह मान लेना पूरी तरह से सुरक्षित है कि जावास्क्रिप्ट हमेशा सिंगल-थ्रेडेड है?
जावास्क्रिप्ट को सभी आधुनिक ब्राउज़र कार्यान्वयन में एकल-थ्रेडेड के रूप में जाना जाता है, लेकिन क्या यह किसी भी मानक में निर्दिष्ट है या यह सिर्फ परंपरा से है? क्या यह मान लेना पूरी तरह से सुरक्षित है कि जावास्क्रिप्ट हमेशा सिंगल-थ्रेडेड है?
जवाबों:
यह एक अच्छा सवाल है। मुझे "हाँ" कहना अच्छा लगेगा। मैं नहीं कर सकता।
जावास्क्रिप्ट को आमतौर पर स्क्रिप्ट (*) के लिए दिखाई देने वाले निष्पादन का एक ही धागा माना जाता है, ताकि जब आपकी इनलाइन स्क्रिप्ट, ईवेंट श्रोता या टाइमआउट दर्ज किया जाए, तब तक आप पूरी तरह से नियंत्रण में रहें जब तक कि आप अपने ब्लॉक या फ़ंक्शन के अंत से वापस नहीं आते।
(*: इस सवाल को अनदेखा करते हुए कि क्या ब्राउज़र वास्तव में एक ओएस-थ्रेड का उपयोग करके अपने जेएस इंजनों को लागू करते हैं, या क्या अन्य सीमित थ्रेड-ऑफ-एक्जीक्यूशन वेबवर्कर्स द्वारा पेश किए जाते हैं।)
हालांकि, वास्तव में यह काफी डरपोक तरीके से सच नहीं है।
सबसे आम मामला तत्काल घटनाओं का है। जब आपका कोड उन्हें पैदा करने के लिए कुछ करता है, तो ब्राउज़र उन्हें तुरंत आग देगा:
var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
l.value+= 'blur\n';
};
setTimeout(function() {
l.value+= 'log in\n';
l.focus();
l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">
log in, blur, log out
आईई को छोड़कर सभी में परिणाम । ये घटनाएँ सिर्फ इसलिए नहीं बुझतीं क्योंकि आपने focus()
सीधे फोन किया, वे हो सकता है क्योंकि आपने फोन किया alert()
, या एक पॉप-अप विंडो खोली, या कुछ और जो फोकस को स्थानांतरित करता है।
यह अन्य घटनाओं में भी परिणाम कर सकता है। उदाहरण के लिए, एक i.onchange
श्रोता को जोड़ें और focus()
कॉल को अनफोकस करने से पहले इनपुट में कुछ लिखें , और लॉग ऑर्डर log in, change, blur, log out
ऑपरा को छोड़कर, जहां यह log in, blur, log out, change
और IE है, जहां यह (यहां तक कि अपेक्षाकृत कम) log in, change, log out, blur
।
इसी तरह click()
एक तत्व पर कॉल करना जो इसे प्रदान करता है onclick
तुरंत सभी ब्राउज़रों में हैंडलर को बुलाता है (कम से कम यह सुसंगत है!)।
(मैं on...
यहां प्रत्यक्ष ईवेंट हैंडलर गुणों का उपयोग कर रहा हूं , लेकिन ऐसा ही होता है addEventListener
और attachEvent
)
ऐसी परिस्थितियों का भी एक समूह है, जिसमें आपके कोड को थ्रेडेड करने के दौरान ईवेंट्स में आग लग सकती है, इसके बावजूद कि आप इसे भड़काने के लिए कुछ नहीं कर रहे हैं। एक उदाहरण:
var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
l.value+= 'alert in\n';
alert('alert!');
l.value+= 'alert out\n';
};
window.onresize= function() {
l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>
मारो alert
और आपको एक मोडल डायलॉग बॉक्स मिलेगा। जब तक आप उस संवाद को खारिज नहीं करते, तब तक कोई और स्क्रिप्ट निष्पादित नहीं होती है? नहीं। मुख्य विंडो का आकार बदलें और आपको alert in, resize, alert out
टेक्स्टारिया में मिलेगा ।
आपको लगता है कि एक मोडल डायलॉग बॉक्स के ऊपर एक विंडो का आकार बदलना असंभव है, लेकिन ऐसा नहीं है: लिनक्स में, आप विंडो को जितना चाहें उतना आकार बदल सकते हैं; विंडोज पर यह इतना आसान नहीं है, लेकिन आप स्क्रीन रिज़ॉल्यूशन को एक बड़े से छोटे में बदल सकते हैं, जहां खिड़की फिट नहीं होती है, जिससे इसे आकार दिया जा सकता है।
आप सोच सकते हैं, ठीक है, यह केवल resize
(और शायद कुछ अधिक पसंद है scroll
) जब उपयोगकर्ता ब्राउज़र के साथ सक्रिय बातचीत नहीं करता है तो आग लग सकती है क्योंकि स्क्रिप्ट थ्रेडेड है। और सिंगल विंडो के लिए आप सही हो सकते हैं। लेकिन जब आप क्रॉस-विंडो स्क्रिप्टिंग कर रहे होते हैं, तो यह सब कुछ बर्तन में चला जाता है। सफारी के अलावा सभी ब्राउज़रों के लिए, जो सभी विंडो / टैब / फ़्रेम को ब्लॉक करता है जब उनमें से कोई एक व्यस्त होता है, तो आप किसी अन्य दस्तावेज़ के कोड से एक दस्तावेज़ के साथ बातचीत कर सकते हैं, निष्पादन के एक अलग थ्रेड में चल रहे हैं और किसी भी संबंधित ईवेंट हैंडलर के लिए आग।
वे जगहें जहाँ आप उत्पन्न होने का कारण बन सकते हैं, स्क्रिप्ट को थ्रेडेड करते हुए उठाया जा सकता है:
जब मोडल पॉपअप ( alert
, confirm
, prompt
), खुले सभी ब्राउज़रों लेकिन ओपेरा में हैं,
showModalDialog
ब्राउज़रों के दौरान जो इसका समर्थन करते हैं;
"इस पृष्ठ पर एक स्क्रिप्ट व्यस्त हो सकती है ..." संवाद बॉक्स, भले ही आप स्क्रिप्ट को चलाना जारी रखने का विकल्प चुनते हैं, आकार बदलने और धुंधला होने जैसी घटनाओं की अनुमति देता है और स्क्रिप्ट के बीच में होने तक भी नियंत्रित किया जा सकता है। ओपेरा में छोड़कर, व्यस्त-पाश।
मेरे लिए कुछ समय पहले, IE में सन जावा प्लगइन के साथ, एप्लेट पर किसी भी तरीके को कॉल करने से ईवेंट्स को आग लगाने और स्क्रिप्ट को फिर से दर्ज करने की अनुमति मिल सकती है। यह हमेशा एक समय-संवेदनशील बग था, और यह संभव है कि सूर्य ने इसे तय किया है (मैं निश्चित रूप से आशा करता हूं)।
शायद अधिक। जब से मैंने इसका परीक्षण किया है, तब से कुछ समय हो गया है और ब्राउज़र ने जटिलता प्राप्त कर ली है।
सारांश में, जावास्क्रिप्ट ज्यादातर उपयोगकर्ताओं को दिखाई देता है, ज्यादातर समय, एक सख्त घटना-चालित निष्पादन का एक धागा होता है। वास्तव में, यह कोई ऐसी बात नहीं है। यह स्पष्ट नहीं है कि यह केवल एक बग और कितना जानबूझकर डिजाइन है, लेकिन यदि आप जटिल एप्लिकेशन, विशेष रूप से क्रॉस-विंडो / फ़्रेम-स्क्रिप्टिंग लिख रहे हैं, तो हर मौका है कि यह आपको काट सकता है - और रुक-रुक कर, कठिन-से-डिबग तरीके।
यदि सबसे बुरा सबसे खराब आता है, तो आप सभी ईवेंट प्रतिक्रियाओं को अप्रत्यक्ष करके समवर्ती समस्याओं को हल कर सकते हैं। जब कोई घटना सामने आती है, तो उसे एक कतार में छोड़ दें और बाद में एक setInterval
समारोह में कतार से निपटें । यदि आप एक ऐसी रूपरेखा लिख रहे हैं जिसे आप जटिल अनुप्रयोगों द्वारा उपयोग करने का इरादा रखते हैं, तो ऐसा करना एक अच्छा कदम हो सकता है। postMessage
उम्मीद है कि भविष्य में क्रॉस-डॉक्यूमेंट स्क्रिप्टिंग के दर्द को भी शांत करेगा।
blur
बाद आपका कोड ब्राउजर पर नियंत्रण लौटाता है।
मैं हां कहूंगा - क्योंकि वस्तुतः सभी मौजूदा (कम से कम सभी गैर-तुच्छ) जावास्क्रिप्ट कोड टूट जाएगा यदि ब्राउज़र का जावास्क्रिप्ट इंजन इसे अतुल्यकालिक रूप से चलाने के लिए था।
इस तथ्य को जोड़ें कि एचटीएमएल 5 पहले से ही वेब वर्कर्स को निर्दिष्ट करता है (मल्टी-थ्रेडिंग जावास्क्रिप्ट कोड के लिए एक स्पष्ट, मानकीकृत एपीआई) जो मूल जावास्क्रिप्ट में मल्टी-थ्रेडिंग को पेश करता है, वह ज्यादातर व्यर्थ होगा।
( अन्य टिप्पणीकारों पर ध्यान दें: भले ही setTimeout/setInterval
, HTTP-request onload इवेंट्स (XHR), और UI इवेंट्स (क्लिक, फ़ोकस आदि) मल्टी-थ्रेडेडनेस की एक क्रूड छाप प्रदान करते हैं - वे अभी भी सभी को एक ही टाइमलाइन के साथ निष्पादित करते हैं - एक पर एक समय - इसलिए भले ही हम उनके निष्पादन आदेश को पहले से न जानते हों, किसी ईवेंट हैंडलर, समयबद्ध फ़ंक्शन या XHR कॉलबैक के निष्पादन के दौरान बाहरी परिस्थितियों को बदलने के बारे में चिंता करने की कोई आवश्यकता नहीं है।)
हां, हालांकि आप किसी भी अतुल्यकालिक APIs जैसे setInterval और xmlhttp कॉलबैक का उपयोग करते समय समवर्ती प्रोग्रामिंग (मुख्य रूप से दौड़ की स्थिति) के कुछ मुद्दों को पीड़ित कर सकते हैं।
मैं कहूंगा कि विनिर्देश किसी को एक इंजन बनाने से नहीं रोकता है जो कई थ्रेड्स पर जावास्क्रिप्ट चलाता है , जिससे कोड को साझा ऑब्जेक्ट स्थिति तक पहुंचने के लिए सिंक्रनाइज़ेशन करने की आवश्यकता होती है।
मुझे लगता है कि एकल-थ्रेडेड नॉन-ब्लॉकिंग प्रतिमान उन ब्राउज़रों में जावास्क्रिप्ट चलाने की आवश्यकता से बाहर आया जहां यूआई कभी भी ब्लॉक नहीं कर सकता।
Nodejs ने ब्राउज़रों के दृष्टिकोण का अनुसरण किया है ।
राइनो इंजन हालांकि, विभिन्न थ्रेड्स में रनिंग जेएस कोड का समर्थन करता है । निष्पादन संदर्भ साझा नहीं कर सकते, लेकिन वे गुंजाइश साझा कर सकते हैं। इस विशिष्ट मामले के लिए दस्तावेज में कहा गया है:
... "राइनो गारंटी देता है कि जावास्क्रिप्ट ऑब्जेक्ट्स के गुणों तक पहुंच धागे के पार परमाणु है, लेकिन एक ही समय में एक ही दायरे में निष्पादित स्क्रिप्ट के लिए कोई और गारंटी नहीं देता है। यदि दो स्क्रिप्ट एक ही स्कोप का एक साथ उपयोग करते हैं, तो स्क्रिप्ट हैं साझा चर के लिए किसी भी पहुँच के समन्वय के लिए जिम्मेदार है । "
राइनो प्रलेखन को पढ़ने से मैं यह निष्कर्ष निकालता हूं कि किसी के लिए एक जावास्क्रिप्ट एपीआई लिखना संभव हो सकता है जो नए जावास्क्रिप्ट थ्रेड्स भी पैदा करता है, लेकिन एपीआई राइनो-विशिष्ट होगा (उदाहरण के लिए नोड केवल एक नई प्रक्रिया को स्पॉन कर सकता है)।
मैं कल्पना करता हूं कि एक ऐसे इंजन के लिए भी जो जावास्क्रिप्ट में कई थ्रेड्स का समर्थन करता है, ऐसे स्क्रिप्ट के साथ संगतता होनी चाहिए जो मल्टी-थ्रेडिंग या ब्लॉकिंग पर विचार नहीं करते हैं।
Concearning ब्राउज़रों और NodeJS तरह से मैं यह है देखें:
तो, ब्राउज़रों और नोडज (और शायद बहुत से अन्य इंजनों) के मामले में जावास्क्रिप्ट को मल्टीथ्रेडेड नहीं किया जाता है, लेकिन इंजन स्वयं होते हैं ।
वेब-वर्करों की मौजूदगी इस बात को और सही ठहराती है कि जावास्क्रिप्ट को बहु-सूत्रित किया जा सकता है, इस अर्थ में कि कोई जावास्क्रिप्ट में कोड बना सकता है जो एक अलग थ्रेड पर चलेगा।
हालाँकि: वेब-वर्कर पारंपरिक थ्रेड्स की समस्याओं पर अंकुश नहीं लगाते हैं जो निष्पादन के संदर्भ को साझा कर सकते हैं। उपरोक्त नियम 2 और 3 अभी भी लागू होते हैं , लेकिन इस बार जावास्क्रिप्ट में उपयोगकर्ता (js कोड लेखक) द्वारा थ्रेडेड कोड बनाया गया है।
विचार करने के लिए केवल एक चीज की संख्या है, जो दक्षता से (और संगामिति नहीं ) दृष्टिकोण से, थ्रेडेड थ्रेड्स की संख्या है । निचे देखो:
वर्कर इंटरफ़ेस वास्तविक OS-लेवल थ्रेड्स को जन्म देता है, और माइंडफुल प्रोग्रामर चिंतित हो सकते हैं कि यदि आप सावधान नहीं हैं तो कंसीडर आपके कोड में "दिलचस्प" प्रभाव पैदा कर सकता है।
हालांकि, चूंकि वेब कर्मचारियों ने संचार बिंदुओं को सावधानीपूर्वक नियंत्रित किया है अन्य थ्रेड्स के साथ , इसलिए यह वास्तव में समसामयिक समस्याओं का कारण है । गैर-थ्रेडसेफ़ घटक या DOM तक कोई पहुँच नहीं है। और आपको क्रमबद्ध ऑब्जेक्ट के माध्यम से एक थ्रेड के अंदर और बाहर विशिष्ट डेटा पास करना होगा। इसलिए आपको अपने कोड में समस्याएं पैदा करने के लिए वास्तव में कड़ी मेहनत करनी होगी।
सिद्धांत के अलावा, स्वीकार किए गए उत्तर पर वर्णित संभावित कोने के मामलों और बगों के बारे में हमेशा तैयार रहें
JavaScript / ECMAScript होस्ट वातावरण के भीतर रहने के लिए डिज़ाइन की गई है। यह है कि, जावास्क्रिप्ट वास्तव में नहीं है कुछ भी कर जब तक मेजबान वातावरण पार्स और किसी दिए गए स्क्रिप्ट को निष्पादित, और पर्यावरण वस्तुओं है कि जाने जावास्क्रिप्ट वास्तव में उपयोगी हो (जैसे ब्राउज़रों में डोम के रूप में) प्रदान करने के लिए फैसला किया।
मुझे लगता है कि एक दिया गया फंक्शन या स्क्रिप्ट ब्लॉक लाइन-बाय-लाइन निष्पादित करेगा और यह जावास्क्रिप्ट के लिए गारंटी है। हालाँकि, शायद एक मेजबान वातावरण एक ही समय में कई स्क्रिप्ट निष्पादित कर सकता है। या, एक मेजबान वातावरण हमेशा एक वस्तु प्रदान कर सकता है जो मल्टी-थ्रेडिंग प्रदान करता है। setTimeout
और setInterval
उदाहरण के लिए, या कम से कम छद्म उदाहरण हैं, एक मेजबान वातावरण के लिए कुछ संगति करने का एक तरीका प्रदान करता है (भले ही यह बिल्कुल संगामिति न हो)।
नहीं।
मैं यहां भीड़ के खिलाफ जा रहा हूं, लेकिन मेरे साथ रहना। एकल JS स्क्रिप्ट को प्रभावी रूप से सिंगल थ्रेडेड करने का इरादा है , लेकिन इसका मतलब यह नहीं है कि इसे अलग तरीके से व्याख्या नहीं किया जा सकता है।
मान लीजिए कि आपके पास निम्नलिखित कोड हैं ...
var list = [];
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
यह इस उम्मीद के साथ लिखा गया है कि लूप के अंत तक, लिस्ट में 10000 प्रविष्टियाँ होनी चाहिए जो कि इंडेक्स स्क्वॉयर हैं, लेकिन VM यह नोटिस कर सकता है कि लूप का प्रत्येक पुनरावृत्ति दूसरे को प्रभावित नहीं करता है, और दो थ्रेड्स का उपयोग करके पुन: व्याख्या करता है।
पहला सूत्र
for (var i = 0; i < 5000; i++) {
list[i] = i * i;
}
दूसरा धागा
for (var i = 5000; i < 10000; i++) {
list[i] = i * i;
}
मैं यहां सरलीकरण कर रहा हूं, क्योंकि जेएस सरणियां अधिक जटिल हैं, फिर मेमोरी के डंबल चुरा लेते हैं, लेकिन अगर ये दोनों स्क्रिप्ट्स थ्रेड-सेफ तरीके से सरणी में प्रविष्टियां जोड़ने में सक्षम हैं, तो तब तक दोनों को निष्पादित किया जा सकता है। एकल-थ्रेडेड संस्करण के समान परिणाम।
जबकि मैं किसी भी वीएम को इस तरह से समानांतर कोड का पता लगाने के बारे में नहीं जानता, यह संभावना है कि यह भविष्य में जेआईटी वीएम के लिए अस्तित्व में आ सकता है, क्योंकि यह कुछ स्थितियों में अधिक गति प्रदान कर सकता है।
इस अवधारणा को आगे बढ़ाते हुए, यह संभव है कि वीएम को बहु-थ्रेडेड कोड में परिवर्तित करने के लिए कोड को एनोटेट किया जा सकता है।
// like "use strict" this enables certain features on compatible VMs.
"use parallel";
var list = [];
// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
चूंकि वेब कार्यकर्ता जावास्क्रिप्ट में आ रहे हैं, इसलिए यह संभावना नहीं है कि यह ... बदसूरत प्रणाली कभी अस्तित्व में आएगी, लेकिन मुझे लगता है कि यह कहना सुरक्षित है कि जावास्क्रिप्ट परंपरा से थ्रेडेड है।
खैर, क्रोम मल्टीप्रोसेस है, और मुझे लगता है कि हर प्रक्रिया अपने स्वयं के जावास्क्रिप्ट कोड के साथ काम करती है, लेकिन जहां तक कोड जानता है, यह "सिंगल-थ्रेडेड" है।
मल्टी-थ्रेडिंग के लिए जावास्क्रिप्ट में कोई भी समर्थन नहीं है, कम से कम स्पष्ट रूप से नहीं, इसलिए इससे कोई फर्क नहीं पड़ता।
मैंने थोड़े संशोधनों के साथ @ bobince का उदाहरण दिया है:
<html>
<head>
<title>Test</title>
</head>
<body>
<textarea id="log" rows="20" cols="40"></textarea>
<br />
<button id="act">Run</button>
<script type="text/javascript">
let l= document.getElementById('log');
let b = document.getElementById('act');
let s = 0;
b.addEventListener('click', function() {
l.value += 'click begin\n';
s = 10;
let s2 = s;
alert('alert!');
s = s + s2;
l.value += 'click end\n';
l.value += `result = ${s}, should be ${s2 + s2}\n`;
l.value += '----------\n';
});
window.addEventListener('resize', function() {
if (s === 10) {
s = 5;
}
l.value+= 'resize\n';
});
</script>
</body>
</html>
इसलिए, जब आप रन दबाते हैं, तो अलर्ट पॉपअप करें और "सिंगल थ्रेड" करें, आपको कुछ इस तरह से देखना चाहिए:
click begin
click end
result = 20, should be 20
लेकिन अगर आप विंडोज पर ओपेरा या फ़ायरफ़ॉक्स में इसे चलाने की कोशिश करते हैं और स्क्रीन पर अलर्ट पॉपअप के साथ विंडो को अधिकतम / अधिकतम करते हैं, तो इसके लिए कुछ करना होगा:
click begin
resize
click end
result = 15, should be 20
मैं यह नहीं कहना चाहता, कि यह "मल्टीथ्रेडिंग" है, लेकिन कुछ कोड ने गलत समय में मेरे साथ यह उम्मीद नहीं की थी, और अब मेरे पास एक भ्रष्ट राज्य है। और इस व्यवहार के बारे में जानना बेहतर होगा।
एक दूसरे के भीतर दो setTimeout फ़ंक्शन घोंसला करने की कोशिश करें और वे बहुपरत व्यवहार करेंगे (यानी, बाहरी टाइमर अपने फ़ंक्शन को निष्पादित करने से पहले आंतरिक एक के पूरा होने की प्रतीक्षा नहीं करेगा)।
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(रिकॉर्ड के लिए, यो डॉग को पहले आना चाहिए, फिर कंसोल लॉग आउटपुट)