ये दोनों शीर्ष-रेटेड उत्तर गलत हैं। कंसीडर मॉडल और इवेंट लूप पर एमडीएन विवरण देखें , और यह स्पष्ट हो जाना चाहिए कि क्या चल रहा है (एमडीएन संसाधन एक वास्तविक रत्न है)। और बस का उपयोग कर setTimeout
इस छोटी सी समस्या को "हल" करने के अलावा अपने कोड में अप्रत्याशित समस्याओं जा सकता है।
वास्तव में क्या है यहाँ पर जा रहा है कि नहीं है "ब्राउज़र पूरी तरह तैयार नहीं हो सकता है अभी तक क्योंकि संगामिति," या के आधार पर कुछ "प्रत्येक पंक्ति एक घटना कतार के पीछे में जुड़ जाता है कि"।
द जैसफिल्ड DVK द्वारा प्रदान की वास्तव में एक समस्या को दिखाता है, लेकिन इसके लिए उसका विवरण सही नहीं है।
उसके कोड में क्या हो रहा है कि वह पहले इवेंट हैंडलर को इवेंट में अटैच कर रहा click
है#do
बटन ।
फिर, जब आप वास्तव में बटन पर क्लिक करते हैं, message
तो ईवेंट हैंडलर फ़ंक्शन का संदर्भ देते हुए बनाया जाता है, जिसे जोड़ा जाता है message queue
। जब event loop
यह संदेश पहुंचता है, तो यह frame
स्टैक पर बनाता है , फ़ंक्शन के साथ jsfiddle में क्लिक इवेंट हैंडलर को कॉल करता है।
और यह वह जगह है जहाँ यह दिलचस्प हो जाता है। हम जावास्क्रिप्ट के अतुल्यकालिक होने के बारे में सोच रहे हैं कि हम इस छोटे से तथ्य को नजरअंदाज करने के लिए प्रवण हैं: किसी भी फ्रेम को निष्पादित किया जाना है, अगले फ्रेम से पहले निष्पादित किया जा सकता है । कोई संगति नहीं, लोग।
इसका क्या मतलब है? इसका मतलब यह है कि जब भी संदेश कतार से किसी फ़ंक्शन को आमंत्रित किया जाता है, तो यह कतार को अवरुद्ध कर देता है जब तक कि यह उत्पन्न होने वाले ढेर को खाली नहीं किया जाता है। या, अधिक सामान्य शब्दों में, यह तब तक अवरुद्ध होता है जब तक कि फ़ंक्शन वापस नहीं आया। और यह सब कुछ ब्लॉक कर देता है DOM रेंडरिंग ऑपरेशंस, स्क्रॉलिंग और व्हाट्सएप सहित सभी । यदि आप पुष्टि चाहते हैं, तो फ़ेडल में लंबे समय से चल रहे ऑपरेशन की अवधि बढ़ाने की कोशिश करें (जैसे कि बाहरी लूप को 10 बार और अधिक बार चलाएं), और आप देखेंगे कि जब यह चलता है, तो आप पृष्ठ को स्क्रॉल नहीं कर सकते। यदि यह बहुत लंबा चलता है, तो आपका ब्राउज़र आपसे पूछेगा कि क्या आप इस प्रक्रिया को मारना चाहते हैं, क्योंकि यह पृष्ठ को अनुत्तरदायी बना रहा है। फ़्रेम निष्पादित किया जा रहा है, और इवेंट लूप और संदेश कतार समाप्त होने तक अटके हुए हैं।
तो पाठ का यह साइड-इफेक्ट अपडेट क्यों नहीं कर रहा है? क्योंकि आप जबकि है डोम में तत्व का मान बदल गया है - आप कर सकते हैं console.log()
अपने मूल्य तुरंत बदलने के बाद और देखते हैं कि यह है बदल दिया गया है (जो शो क्यों DVK के विवरण सही नहीं है) - ब्राउज़र व्यय करने के लिए ढेर के लिए प्रतीक्षा कर रहा है ( on
हैंडलर फ़ंक्शन वापस लौटने के लिए) और इस प्रकार संदेश को समाप्त करने के लिए, ताकि यह अंततः उस संदेश को निष्पादित करने के लिए प्राप्त हो सके जो रनटाइम द्वारा हमारे म्यूटेशन ऑपरेशन की प्रतिक्रिया के रूप में जोड़ा गया है, और यूआई में उस उत्परिवर्तन को प्रतिबिंबित करने के लिए। ।
ऐसा इसलिए है क्योंकि हम वास्तव में कोड खत्म होने का इंतजार कर रहे हैं। हमने यह नहीं कहा है कि "कोई व्यक्ति इसे प्राप्त करता है और फिर परिणाम के साथ इस फ़ंक्शन को बुलाता है, धन्यवाद, और अब मैं इतना imma किया हूं, अब जो कुछ भी करना है," जैसे हम आमतौर पर हमारे घटना-आधारित अतुल्यकालिक जावास्क्रिप्ट के साथ करते हैं। हम एक क्लिक ईवेंट हैंडलर फ़ंक्शन दर्ज करते हैं, हम एक DOM तत्व को अपडेट करते हैं, हम एक अन्य फ़ंक्शन को कॉल करते हैं, दूसरा फ़ंक्शन लंबे समय तक काम करता है और फिर लौटता है, हम फिर उसी DOM तत्व को अपडेट करते हैं, और फिर हम प्रारंभिक फ़ंक्शन से प्रभावी रूप से खाली हो जाते हैं। ढेर। और फिर ब्राउज़र कतार में अगले संदेश को प्राप्त कर सकता है, जो कुछ आंतरिक "ऑन-डोम-म्यूटेशन" प्रकार की घटना को ट्रिगर करके हमारे द्वारा उत्पन्न संदेश हो सकता है।
ब्राउज़र UI तब तक यूआई को अपडेट नहीं कर सकता (या नहीं चुनता है) जब तक कि वर्तमान में निष्पादित फ्रेम पूरा नहीं हो जाता है (फ़ंक्शन वापस आ गया है)। व्यक्तिगत रूप से, मुझे लगता है कि यह प्रतिबंध के बजाय डिजाइन द्वारा है।
setTimeout
फिर काम क्यों करता है ? यह ऐसा करता है, क्योंकि यह प्रभावी ढंग से कॉल को अपने ही फ्रेम से लंबे समय तक चलने वाले फ़ंक्शन को हटा देता है, इसे बाद में window
संदर्भ में निष्पादित करने के लिए शेड्यूल करता है, ताकि यह स्वयं तुरंत वापस आ सके और संदेश कतार को अन्य संदेशों को संसाधित करने की अनुमति दे सके। और विचार यह है कि UI "अपडेट पर" संदेश जो जावास्क्रिप्ट में हमारे द्वारा ट्रिगर किया गया है जब DOM में पाठ बदलते हुए अब लंबे समय से चल रहे फ़ंक्शन के लिए पंक्तिबद्ध किए गए संदेश से आगे है, ताकि UI अपडेट हमारे ब्लॉक होने से पहले हो जाए लंबे समय के लिए।
ध्यान दें कि a) लंबे समय से चल रहा फ़ंक्शन अभी भी सब कुछ ब्लॉक करता है जब यह चलता है, और b) आपको यह गारंटी नहीं है कि UI अपडेट वास्तव में संदेश कतार में इसके आगे है। मेरे जून 2018 के क्रोम ब्राउजर पर, 0
फिडेल द्वारा प्रदर्शित की जाने वाली समस्या को "ठीक" नहीं करता है - 10 करता है। मैं वास्तव में इससे थोड़ा लड़खड़ाया हुआ हूं, क्योंकि यह मेरे लिए तर्कसंगत लगता है कि यूआई अपडेट संदेश को इससे पहले कतारबद्ध किया जाना चाहिए, क्योंकि इसके ट्रिगर को "बाद में" चलाने के लिए लंबे समय से चलने वाले फ़ंक्शन को शेड्यूल करने से पहले निष्पादित किया जाता है। लेकिन शायद V8 इंजन में कुछ अनुकूलन हैं जो हस्तक्षेप कर सकते हैं, या शायद मेरी समझ में बस कमी है।
ठीक है, तो उपयोग करने में क्या समस्या है setTimeout
, और इस विशेष मामले के लिए एक बेहतर समाधान क्या है?
सबसे पहले, setTimeout
इस तरह के किसी भी घटना हैंडलर का उपयोग करने के साथ समस्या, एक और समस्या को कम करने की कोशिश करने के लिए, अन्य कोड के साथ गड़बड़ करने का खतरा है। यहाँ मेरे काम से एक वास्तविक जीवन उदाहरण है:
किसी सहकर्मी ने इवेंट लूप पर गलत सूचना दी, setTimeout 0
उसके प्रतिपादन के लिए कुछ टेम्प्लेट रेंडरिंग कोड का उपयोग करके "थ्रेड" जावास्क्रिप्ट की कोशिश की । वह अब पूछने के लिए यहां नहीं है, लेकिन मैं यह मान सकता हूं कि शायद उसने प्रतिपादन की गति (जो कि कार्यों की वापसी immediacy होगी) को गेज करने के लिए डाला और पाया कि इस दृष्टिकोण का उपयोग उस फ़ंक्शन से तेजी से तेजी से प्रतिक्रियाओं के लिए करेगा।
पहली समस्या स्पष्ट है; आप जावास्क्रिप्ट को थ्रेड नहीं कर सकते हैं, इसलिए आप यहां कुछ भी नहीं जीतते हैं, जबकि आप ओफिसकेशन जोड़ते हैं। दूसरे, आपने अब संभावित ईवेंट श्रोताओं के ढेर से एक टेम्प्लेट के प्रतिपादन को प्रभावी ढंग से अलग कर लिया है जो उम्मीद कर सकते हैं कि बहुत ही टेम्प्लेट का प्रतिपादन किया गया है, जबकि यह बहुत अच्छी तरह से नहीं हो सकता है। उस फ़ंक्शन का वास्तविक व्यवहार अब गैर-नियतात्मक था, जैसा कि - अनजाने में ऐसा था - कोई भी फ़ंक्शन जो इसे चलाएगा, या इस पर निर्भर करेगा। आप शिक्षित अनुमान लगा सकते हैं, लेकिन आप इसके व्यवहार के लिए ठीक से कोड नहीं कर सकते हैं।
एक नया ईवेंट हैंडलर लिखते समय "फिक्स" जो कि उसके तर्क पर निर्भर था, का भी उपयोग करना था setTimeout 0
। लेकिन, यह तय नहीं है, यह समझना मुश्किल है, और यह इस तरह कोड के कारण होने वाली त्रुटियों को डीबग करने में कोई मज़ा नहीं है। कभी-कभी कोई समस्या नहीं होती है, अन्य समय यह लगातार विफल हो जाता है, और फिर फिर कभी-कभी यह काम करता है और छिटपुट रूप से टूटता है, यह मंच के वर्तमान प्रदर्शन पर निर्भर करता है और जो कुछ भी उस समय चल रहा है। यही कारण है कि मैं व्यक्तिगत रूप से इस हैक (यह उपयोग करने के खिलाफ सलाह देंगे है , एक हैक, और हम सभी को पता होना चाहिए यह है कि) जब तक आप वास्तव में पता है कि तुम क्या कर रहे हैं और परिणाम क्या कर रहे हैं।
लेकिन हम इसके बजाय क्या कर सकते हैं? ठीक है, जैसा कि संदर्भित एमडीएन लेख बताता है, या तो काम को कई संदेशों में विभाजित करें (यदि आप कर सकते हैं) ताकि अन्य संदेश जो कतारबद्ध हैं, आपके काम के साथ बातचीत हो सकती है और इसे चलाते समय निष्पादित किया जा सकता है, या एक वेब कार्यकर्ता का उपयोग कर सकता है, जो चल सकता है अपने पृष्ठ के साथ अग्रानुक्रम में और उसकी गणना के साथ किए जाने पर परिणाम लौटाते हैं।
ओह, और अगर आप सोच रहे हैं, "ठीक है, तो क्या मैं इसे अतुल्यकालिक बनाने के लिए लंबे समय तक चलने वाले फ़ंक्शन में कॉलबैक नहीं डाल सकता ?," नहीं। कॉलबैक इसे अतुल्यकालिक नहीं बनाता है, यह स्पष्ट रूप से आपके कॉलबैक को कॉल करने से पहले लंबे समय तक चलने वाला कोड चलाना होगा।