जावास्क्रिप्ट में अतुल्यकालिक समझ [सुखजिंदर अरोड़ा का अनुवाद]

नमस्कार, हेब्र! मैं आपको सुखजिंदर अरोड़ा के लेख "अंडरस्टैंडिंग एसिंक्रोनस जावास्क्रिप्ट" का अनुवाद प्रस्तुत करता हूं।



अनुवाद के लेखक से: मुझे आशा है कि इस लेख का अनुवाद आपको कुछ नया और उपयोगी बताने में मदद करेगा। यदि लेख ने आपकी मदद की, तो आलसी मत बनो और मूल के लेखक को धन्यवाद दें। मैं एक पेशेवर अनुवादक होने का दिखावा नहीं करता, मैं सिर्फ लेखों का अनुवाद करना शुरू कर रहा हूं और किसी भी सार्थक प्रतिक्रिया से खुश रहूंगा।

जावास्क्रिप्ट एक एकल-थ्रेडेड प्रोग्रामिंग भाषा है जिसमें एक समय में केवल एक ही चीज को निष्पादित किया जा सकता है। यही है, एक एकल धागे में, जावास्क्रिप्ट इंजन केवल एक बार में 1 कथन को संसाधित कर सकता है।

हालाँकि सिंगल-थ्रेडेड भाषाएं कोड लिखना आसान बनाती हैं, क्योंकि आपको समसामयिक मुद्दों के बारे में चिंता करने की ज़रूरत नहीं है, लेकिन इसका मतलब यह भी है कि आप लंबे ऑपरेशन को चलाने में सक्षम नहीं होंगे जैसे कि मुख्य थ्रेड को अवरुद्ध किए बिना नेटवर्क तक पहुंचना।

कुछ डेटा के लिए एक एपीआई अनुरोध सबमिट करें। स्थिति के आधार पर, सर्वर को आपके अनुरोध को संसाधित करने में कुछ समय लग सकता है, जबकि मुख्य धारा का निष्पादन अवरुद्ध हो जाएगा, जिसके कारण आपका वेब पेज इसके अनुरोधों का जवाब देना बंद कर देगा।

यह वह जगह है जहाँ जावास्क्रिप्ट अतुल्यकालिक खेल में आता है। जावास्क्रिप्ट अतुल्यकालिक (कॉलबैक, वादे, और async / प्रतीक्षा) का उपयोग करके आप मुख्य धागे को अवरुद्ध किए बिना लंबे नेटवर्क अनुरोध कर सकते हैं।

हालाँकि एक अच्छा जावास्क्रिप्ट डेवलपर होने के लिए इन सभी अवधारणाओं को सीखना आवश्यक नहीं है, फिर भी उन्हें जानना उपयोगी है।

तो, आगे की हलचल के बिना, चलिए शुरू करते हैं।

सिंक्रोनस जावास्क्रिप्ट कैसे काम करती है?


इससे पहले कि हम अतुल्यकालिक जावास्क्रिप्ट के काम में लगें, आइए पहले समझते हैं कि जावास्क्रिप्ट इंजन के अंदर सिंक्रोनस कोड कैसे चलता है। उदाहरण के लिए:

const second = () => { console.log('Hello there!'); } const first = () => { console.log('Hi there!'); second(); console.log('The End'); } first(); 

जावास्क्रिप्ट इंजन के अंदर ऊपर दिए गए कोड को कैसे निष्पादित किया जाता है, यह समझने के लिए, हमें निष्पादन संदर्भ और कॉल स्टैक (जिसे निष्पादन स्टैक के रूप में भी जाना जाता है) की अवधारणा को समझने की आवश्यकता है।

निष्पादन का संदर्भ


निष्पादन संदर्भ पर्यावरण की एक अमूर्त अवधारणा है जिसमें कोड का मूल्यांकन और निष्पादन किया जाता है। जब भी कोई कोड जावास्क्रिप्ट में निष्पादित किया जाता है, तो यह निष्पादन के संदर्भ में चलता है।

फ़ंक्शन कोड को फ़ंक्शन निष्पादन के संदर्भ में निष्पादित किया जाता है, और वैश्विक कोड, बदले में वैश्विक निष्पादन संदर्भ के अंदर निष्पादित होता है। प्रत्येक फ़ंक्शन का अपना निष्पादन संदर्भ होता है।

कॉल स्टैक


कॉल स्टैक एक LIFO संरचना (लास्ट इन, फर्स्ट आउट, फर्स्ट यूज़) के साथ एक स्टैक है, जिसका उपयोग कोड निष्पादन के दौरान बनाए गए सभी निष्पादन संदर्भों को संग्रहीत करने के लिए किया जाता है।

जावास्क्रिप्ट में केवल एक कॉल स्टैक है, क्योंकि यह एकल-थ्रेडेड प्रोग्रामिंग भाषा है। LIFO संरचना का मतलब है कि तत्वों को केवल जोड़ दिया जा सकता है और ढेर के ऊपर से हटाया जा सकता है।

चलिए अब ऊपर दिए गए कोड स्निपेट पर वापस जाते हैं और यह समझने की कोशिश करते हैं कि जावास्क्रिप्ट इंजन कैसे प्रदर्शन करता है।

 const second = () => { console.log('Hello there!'); } const first = () => { console.log('Hi there!'); second(); console.log('The End'); } first(); 



तो यहाँ क्या हुआ?


जब कोड निष्पादित होना शुरू हुआ, तो एक वैश्विक निष्पादन संदर्भ बनाया गया ( मुख्य () के रूप में प्रतिनिधित्व) और कॉल स्टैक के शीर्ष पर जोड़ा गया। जब पहली () फ़ंक्शन पर कॉल आती है, तो इसे स्टैक के शीर्ष पर भी जोड़ा जाता है।

अगला, कंसोल.लॉग ('हाय वहाँ!') कॉल स्टैक के शीर्ष पर रखा गया है, निष्पादन के बाद इसे स्टैक से हटा दिया जाता है। उसके बाद, हम दूसरे () फ़ंक्शन को कॉल करते हैं, इसलिए इसे स्टैक के शीर्ष पर रखा गया है।

कंसोल.लॉग ('हेलो वहाँ!') को स्टैक के शीर्ष पर जोड़ा जाता है और निष्पादन के पूरा होने पर इसे हटा दिया जाता है। दूसरा () फ़ंक्शन पूरा हो गया है, इसे स्टैक से भी हटा दिया गया है।

कंसोल.लॉग ('द एंड') को स्टैक के शीर्ष पर जोड़ा गया और अंत में हटा दिया गया। उसके बाद, पहला () फ़ंक्शन समाप्त हो जाता है और स्टैक से भी हटा दिया जाता है।

प्रोग्राम निष्पादन समाप्त हो जाता है, इसलिए स्टैक से वैश्विक कॉल संदर्भ ( मुख्य () ) हटा दिया जाता है।

अतुल्यकालिक जावास्क्रिप्ट कैसे काम करता है?


अब जब हमें कॉल स्टैक की एक बुनियादी समझ है और सिंक्रोनस जावास्क्रिप्ट कैसे काम करता है, आइए हम अतुल्यकालिक जावास्क्रिप्ट पर वापस जाएं।

अवरोध क्या है?


मान लेते हैं कि हम छवि प्रसंस्करण या नेटवर्क अनुरोध को सिंक्रोनाइज़ कर रहे हैं। उदाहरण के लिए:

 const processImage = (image) => { /** *    **/ console.log('Image processed'); } const networkRequest = (url) => { /** *      **/ return someData; } const greeting = () => { console.log('Hello World'); } processImage(logo.jpg); networkRequest('www.somerandomurl.com'); greeting(); 

छवि प्रसंस्करण और नेटवर्क अनुरोध में समय लगता है। जब processImage () फ़ंक्शन को बुलाया जाता है, तो छवि के आकार के आधार पर इसके निष्पादन में कुछ समय लगेगा।

जब ProcessImage () फ़ंक्शन पूरा हो जाता है, तो इसे स्टैक से हटा दिया जाता है। इसके बाद, networkRequest () फ़ंक्शन को कॉल किया जाता है और स्टैक में जोड़ा जाता है। यह फिर से निष्पादन को पूरा करने से पहले कुछ समय लेगा।

अंत में, जब networkRequest () फ़ंक्शन निष्पादित होता है, तो ग्रीटिंग () फ़ंक्शन को कहा जाता है, क्योंकि इसमें केवल कंसोल.लॉग विधि शामिल है, और यह विधि आमतौर पर जल्दी से चलती है, ग्रीटिंग () फ़ंक्शन तुरंत निष्पादित और समाप्त हो जाएगा।

जैसा कि आप देख सकते हैं, हमें फ़ंक्शन (जैसे कि processImage () या networkRequest () ) को पूरा करने के लिए प्रतीक्षा करने की आवश्यकता है। इसका मतलब है कि ऐसे फ़ंक्शन कॉल स्टैक या मुख्य थ्रेड को ब्लॉक करते हैं। परिणामस्वरूप, जब तक ऊपर कोड निष्पादित नहीं किया जाता है, हम अन्य ऑपरेशन नहीं कर सकते हैं।

तो उपाय क्या है?


सबसे सरल समाधान अतुल्यकालिक कॉलबैक फ़ंक्शन है। हम उनका उपयोग अपने कोड को गैर-अवरोधक बनाने के लिए करते हैं। उदाहरण के लिए:

 const networkRequest = () => { setTimeout(() => { console.log('Async Code'); }, 2000); }; console.log('Hello World'); networkRequest(); 

यहां मैंने नेटवर्क अनुरोध का अनुकरण करने के लिए सेटटाइमआउट विधि का उपयोग किया। कृपया याद रखें कि सेटटाइमआउट जावास्क्रिप्ट इंजन का हिस्सा नहीं है, यह तथाकथित वेब एपीआई (ब्राउज़र में) और सी / सी ++ एपीआई (नोड में) का हिस्सा है।

यह समझने के लिए कि इस कोड को कैसे निष्पादित किया जाता है, हमें कुछ और अवधारणाओं से निपटना होगा, जैसे कि ईवेंट लूप और कॉलबैक कतार (जिसे कार्य कतार या संदेश कतार के रूप में भी जाना जाता है)।



ईवेंट लूप, वेब API और संदेश कतार / कार्य कतार जावास्क्रिप्ट इंजन का हिस्सा नहीं हैं, वे Nodejs (Nodejs के मामले में) में जावास्क्रिप्ट जावास्क्रिप्ट रनटाइम या जावास्क्रिप्ट रनटाइम का हिस्सा हैं। Nodejs में, वेब API को C / C ++ API से बदल दिया जाता है।

अब, ऊपर दिए गए कोड पर वापस जाते हैं और देखते हैं कि एसिंक्रोनस निष्पादन के मामले में क्या होता है।

 const networkRequest = () => { setTimeout(() => { console.log('Async Code'); }, 2000); }; console.log('Hello World'); networkRequest(); console.log('The End'); 



जब उपरोक्त कोड को ब्राउज़र में लोड किया जाता है, तो कंसोल.लॉग ('हैलो वर्ल्ड') को स्टैक में जोड़ा जाता है और निष्पादन पूरा होने पर इसे हटा दिया जाता है। अगला, networkRequest () फ़ंक्शन के लिए कॉल का सामना करना पड़ा है , यह स्टैक के शीर्ष पर जोड़ा गया है।

अगला, सेटटाइमआउट () फ़ंक्शन को कहा जाता है और स्टैक के शीर्ष पर रखा जाता है। सेटटाइमआउट () फ़ंक्शन के 2 तर्क हैं: 1) एक कॉलबैक फ़ंक्शन और 2) मिलीसेकंड में समय।

सेटटाइमआउट () वेब एपीआई वातावरण में 2 सेकंड के लिए एक टाइमर शुरू करता है। इस बिंदु पर, सेटटाइमआउट () पूरा हो जाता है और स्टैक से हटा दिया जाता है। उसके बाद, कंसोल.लॉग ('द एंड') को स्टैक में जोड़ा जाता है, पूरा होने पर इसे निष्पादित और हटा दिया जाता है।

इस बीच, टाइमर समाप्त हो गया है, अब कॉलबैक को संदेश कतार में जोड़ा जाता है। लेकिन कॉलबैक को तुरंत निष्पादित नहीं किया जा सकता है, और यह यहां है कि इवेंट लूप प्रक्रिया में प्रवेश करता है।

घटना पाश


ईवेंट लूप का कार्य कॉल स्टैक का ट्रैक रखना है और निर्धारित करना है कि यह खाली है या नहीं। यदि कॉल स्टैक खाली है, तो ईवेंट लूप संदेश कतार में दिखता है यह देखने के लिए कि क्या कॉलबैक हैं जो पूरा होने की प्रतीक्षा कर रहे हैं।

हमारे मामले में, संदेश कतार में एक कॉलबैक होता है, और निष्पादन स्टैक खाली होता है। इसलिए, ईवेंट लूप स्टैक के शीर्ष पर कॉलबैक जोड़ता है।

कंसोल.लॉग ('Async कोड') के बाद, स्टैक के शीर्ष पर जोड़ा जाता है, इसे निष्पादित और हटाया जाता है। इस बिंदु पर, कॉलबैक पूरा हो गया है और स्टैक से हटा दिया गया है, और कार्यक्रम पूरी तरह से पूरा हो गया है।

डोम इवेंट्स


मैसेज कतार में DOM इवेंट्स से कॉलबैक भी होते हैं, जैसे क्लिक और कीबोर्ड इवेंट। उदाहरण के लिए:

 document.querySelector('.btn').addEventListener('click',(event) => { console.log('Button Clicked'); }); 

DOM ईवेंट्स के मामले में, ईवेंट हैंडलर वेब API से घिरा हुआ है, एक विशिष्ट ईवेंट (इस मामले में, एक क्लिक) की प्रतीक्षा कर रहा है, और जब यह ईवेंट होता है, तो कॉलबैक फ़ंक्शन को संदेश कतार में रखा जाता है, इसके निष्पादन की प्रतीक्षा कर रहा है।

हमने सीखा कि कैसे अतुल्यकालिक कॉलबैक और डीओएम घटनाओं को निष्पादित किया जाता है, जो कॉलबैक को निष्पादित करने के लिए प्रतीक्षा करने के लिए एक संदेश कतार का उपयोग करते हैं।

ES6 MicroTask कतार


लगभग। अनुवाद लेखक: लेख में, लेखक ने संदेश / कार्य कतार और नौकरी / माइक्रो-टेक कतार का उपयोग किया था, लेकिन यदि आप कार्य कतार और नौकरी कतार का अनुवाद करते हैं, तो सिद्धांत रूप में यह एक ही बात करता है। मैंने अनुवाद के लेखक के साथ बात की और नौकरी की कतार की अवधारणा को छोड़ने का फैसला किया। यदि आपके पास इस बारे में कोई विचार है, तो मैं टिप्पणियों में आपकी प्रतीक्षा कर रहा हूं

उसी लेखक के वादों के द्वारा लेख के अनुवाद से लिंक


ईएस 6 ने माइक्रोटस्क क्यू की अवधारणा पेश की, जिसका उपयोग जावास्क्रिप्ट में प्रॉमिस द्वारा किया जाता है। संदेश कतार और माइक्रोटस्क कतार के बीच का अंतर यह है कि संदेश कतार की तुलना में माइक्रोटस्क कतार की प्राथमिकता अधिक होती है, जिसका अर्थ है कि संदेश कतार में कॉलबैक की तुलना में माइक्रोटस्क कतार के अंदर "वादों" को निष्पादित किया जाएगा।

उदाहरण के लिए:

 console.log('Script start'); setTimeout(() => { console.log('setTimeout'); }, 0); new Promise((resolve, reject) => { resolve('Promise resolved'); }).then(res => console.log(res)) .catch(err => console.log(err)); console.log('Script End'); 

निष्कर्ष:

 Script start Script End Promise resolved setTimeout 

जैसा कि आप देख सकते हैं, "वादा" को सेटटाइमआउट से पहले निष्पादित किया गया था, यह सब क्योंकि "वादे" की प्रतिक्रिया माइक्रोस्टाक कतार के अंदर संग्रहीत होती है, जिसमें संदेश कतार की तुलना में अधिक प्राथमिकता होती है।

आइए निम्न उदाहरण देखें, इस बार 2 "वादे" और 2 सेटटाइमआउट :

 console.log('Script start'); setTimeout(() => { console.log('setTimeout 1'); }, 0); setTimeout(() => { console.log('setTimeout 2'); }, 0); new Promise((resolve, reject) => { resolve('Promise 1 resolved'); }).then(res => console.log(res)) .catch(err => console.log(err)); new Promise((resolve, reject) => { resolve('Promise 2 resolved'); }).then(res => console.log(res)) .catch(err => console.log(err)); console.log('Script End'); 

निष्कर्ष:

 Script start Script End Promise 1 resolved Promise 2 resolved setTimeout 1 setTimeout 2 

और फिर, हमारे दोनों "वादों" को सेटटाइम के अंदर कॉलबैक से पहले निष्पादित किया गया था, क्योंकि इवेंट प्रोसेसिंग लूप संदेश कतार / कार्य कतार से कार्यों की तुलना में माइक्रोटस्क कतार से कार्यों को अधिक महत्वपूर्ण मानता है।

यदि कार्यों के निष्पादन के दौरान एक और "वादा" माइक्रोटस्क कतार से प्रकट होता है, तो इसे इस कतार के अंत में जोड़ा जाएगा और संदेश कतार से कॉलबैक से पहले निष्पादित किया जाएगा, और इससे कोई फर्क नहीं पड़ता कि वे अपने निष्पादन के लिए कितनी देर तक प्रतीक्षा करते हैं।

उदाहरण के लिए:

 console.log('Script start'); setTimeout(() => { console.log('setTimeout'); }, 0); new Promise((resolve, reject) => { resolve('Promise 1 resolved'); }).then(res => console.log(res)); new Promise((resolve, reject) => { resolve('Promise 2 resolved'); }).then(res => { console.log(res); return new Promise((resolve, reject) => { resolve('Promise 3 resolved'); }) }).then(res => console.log(res)); console.log('Script End'); 

निष्कर्ष:

 Script start Script End Promise 1 resolved Promise 2 resolved Promise 3 resolved setTimeout 

इस प्रकार, संदेश कतार से कार्यों से पहले माइक्रोटस्क कतार से सभी कार्य पूरे हो जाएंगे। यही है, इवेंट प्रोसेसिंग लूप पहले माइक्रोटस्क क्यू को क्लियर करेगा, और उसके बाद ही मैसेज कतार से कॉलबैक निष्पादित करना शुरू करेगा।

निष्कर्ष


इसलिए, हमने सीखा कि अतुल्यकालिक जावास्क्रिप्ट कैसे काम करता है और अवधारणाएं: कॉल स्टैक, इवेंट लूप, संदेश कतार / कार्य कतार, और माइक्रोटस्क कतार जो जावास्क्रिप्ट रनटाइम बनाते हैं।

Source: https://habr.com/ru/post/hi439620/


All Articles