2011-08-30 18 views
11

मान लीजिए कि मैं कुछ फ़्लैश फिल्म लोड करता हूं जो मुझे भविष्य में किसी बिंदु पर window.flashReady पर कॉल करेगा और window.flashReadyTriggered = true सेट करेगा।क्या मुझे एसिंक्रोनस जावास्क्रिप्ट के साथ दौड़ की स्थिति से चिंतित होने की आवश्यकता है?

अब मेरे पास कोड का एक ब्लॉक है जिसे मैं फ़्लैश तैयार होने पर निष्पादित करना चाहता हूं। मैं इसे तुरंत निष्पादित करना चाहता हूं अगर window.flashReady पहले से ही कॉल कर दिया गया है और मैं इसे window.flashReady में कॉलबैक के रूप में रखना चाहता हूं अगर इसे अभी तक नहीं कहा गया है। अनुभवहीन दृष्टिकोण है इस:

if(window.flashReadyTriggered) { 
    block(); 
} else { 
    window.flashReady = block; 
} 

तो चिंता का विषय है कि मैं इस पर आधारित है कि if हालत में अभिव्यक्ति false लिए मूल्यांकन किया जाता है, लेकिन फिर से पहले block() क्रियान्वित किया जा सकता, window.flashReady बाहरी फ्लैश से शुरू हो रहा है। नतीजतन, block कभी नहीं कहा जाता है।

क्या उच्च स्तर के लक्ष्य को पूरा करने के लिए एक बेहतर डिज़ाइन पैटर्न है (उदाहरण के लिए, मैन्युअल रूप से flashReady कॉलबैक को कॉल करना)? यदि नहीं, तो क्या मैं सुरक्षित हूं, या क्या मुझे अन्य चीजें करनी चाहिए?

+0

क्यों फ्लैश कॉल जावास्क्रिप्ट फ़ंक्शन नहीं है? – zellio

उत्तर

13

जावास्क्रिप्ट एकल धागा है। कोई दौड़ की स्थिति नहीं है।

जब आपके वर्तमान "निर्देश सूचक" पर निष्पादित करने के लिए कोई और कोड नहीं है, तो "थ्रेड" "बैटन पास करता है", और एक पंक्तिबद्ध window.setTimeout या ईवेंट हैंडलर अपना कोड निष्पादित कर सकता है।

आपको जावास्क्रिप्ट के एकल-थ्रेडिंग दृष्टिकोण node.js के डिज़ाइन विचारों को पढ़ने के लिए बेहतर समझ मिलेगी।

अतिरिक्त पठन: Why doesn't JavaScript support multithreading?

+0

संक्षिप्त उत्तर के लिए धन्यवाद जो सीधे बिंदु पर जाता है। मुझे लगता है कि शुरुआत से मुझे इस तरह से अपना प्रश्न phrased करना चाहिए था। –

+0

यह उत्तर पूरी कहानी नहीं है। कृपया नीचे मेरा उत्तर पढ़ें: http://stackoverflow.com/a/12799287/1040124 – Jens

+0

@ जेन्स, आने वाली घटनाएं वर्तमान कोड को पसंद नहीं करेंगे, लेकिन आप सही हैं कि दौड़ें हैं जो कॉलबैक अगली बार आग लगती हैं। – kay

17

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

इस प्रकार सामान्य दौड़ की स्थिति जो निष्पादन के दो धागे के कारण होती है, उसी समय जावास्क्रिप्ट में नहीं होती है।

यह सब सहित जावास्क्रिप्ट घटनाओं के रूपों में शामिल हैं: (आदि .. माउस, चाबियाँ,) आदि उपयोगकर्ता घटनाओं, टाइमर घटनाओं, नेटवर्क घटनाओं (ajax कॉलबैक), ...

केवल जगह आप वास्तव में क्या कर सकते हैं जावास्क्रिप्ट में बहु-थ्रेडिंग HTML5 Web Workers के साथ है, लेकिन वे नियमित जावास्क्रिप्ट से बहुत अलग हैं (वे केवल संदेश पासिंग के माध्यम से नियमित जावास्क्रिप्ट के साथ संवाद कर सकते हैं) और डीओएम में हस्तक्षेप नहीं कर सकते हैं और उनकी अपनी स्क्रिप्ट और नेमस्पेस आदि होनी चाहिए ...


जबकि मैं तकनीकी रूप से इसे दौड़ की स्थिति नहीं कहूंगा, वहां स्थिति होगी जावास्क्रिप्ट में आयनों में से कुछ एसिंक्रोनस ऑपरेशंस के कारण जहां आप दो या दो से अधिक असिंक्रोनस ऑपरेशंस चल सकते हैं और यह तब तक अप्रत्याशित हो सकता है जब प्रत्येक ऑपरेशन दूसरों के सापेक्ष पूरा हो जाए।यह समय की अनिश्चितता पैदा करता है (यदि आपके कोड के लिए संचालन का सापेक्ष समय महत्वपूर्ण है) तो कुछ ऐसा बनाता है जिसके लिए आपको मैन्युअल रूप से कोड करना है। आपको परिचालनों को अनुक्रमित करने की आवश्यकता हो सकती है ताकि एक रन चल सके और आप सचमुच अगले को शुरू करने से पहले इसे पूरा करने के लिए प्रतीक्षा कर सकते हैं। या, आप सभी तीन परिचालन शुरू कर सकते हैं और उसके बाद कुछ कोड हैं जो सभी तीन परिणामों को एकत्र करते हैं और जब वे सभी तैयार होते हैं, तो आपका कोड बढ़ जाता है।

आधुनिक जावास्क्रिप्ट में, आमतौर पर इन प्रकार के एसिंक्रोनस ऑपरेशंस को प्रबंधित करने के लिए वादे का उपयोग किया जाता है।

तो, अगर आपको लगता है कि प्रत्येक एक वादा वापसी (आदि किसी डेटाबेस से पढ़ने, किसी अन्य सर्वर से एक अनुरोध प्राप्त करते समय, ...) की तरह तीन अतुल्यकालिक संचालन किया था, तो आप मैन्युअल तो इस तरह क्रम सकता है:

a().then(b).then(c).then(result => { 
    // result here 
}).catch(err => { 
    // error here 
}); 

या, यदि आप उन सब (सभी उड़ान में एक ही समय में) एक साथ चलाने के लिए और बस जब वे सब कर रहे थे पता था, तुम कर सकते हो:

Promise.all([a(), b(), c()])..then(results => { 
    // results here 
}).catch(err => { 
    // error here 
}); 

जब मैं इन दौड़ की स्थिति फोन नहीं होगा, वे इंडे को नियंत्रित करने के लिए आपके कोड को डिजाइन करने के समान परिवार में हैं अनुक्रमण अनुक्रमित करें।


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

  1. क्लिक
  2. क्लिक ईवेंट हैंडलर परिवर्तन एक और फ़ील्ड पर ध्यान अन्य क्षेत्र onFocus के लिए एक ईवेंट हैंडलर है
  3. कि
  4. ब्राउज़र कॉल onFocus ईवेंट हैंडलर तुरंत
  5. ऑनफोकस इवेंट हैंडलर
  6. शेष क्लिक इवेंट हैंडलर चलाता है (। फोकस() कॉल के बाद)

यह तकनीकी रूप से दौड़ की स्थिति नहीं है क्योंकि यह 100% ज्ञात है जब ऑनफोकस ईवेंट हैंडलर निष्पादित करेगा (.focus() कॉल के दौरान)। लेकिन, यह एक ऐसी स्थिति पैदा कर सकता है जहां एक इवेंट हैंडलर चलता है जबकि दूसरा निष्पादन के बीच में होता है।

+0

"निष्पादन का कोई व्यक्तिगत धागा" <--- वह क्या है? क्या यह एक एकल घटना लूप पुनरावृत्ति है? या यह एक एकल घटना हैंडलर निष्पादन है? – Vanuan

+1

आपको परिभाषित करने की आवश्यकता है कि दौड़ की स्थिति क्या है। रेस की स्थिति में धागे से कोई लेना देना नहीं है। – Vanuan

+0

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

7

यह ध्यान रखना महत्वपूर्ण है कि यदि आप उदाहरण के लिए भी दौड़ की स्थिति का अनुभव कर सकते हैं। एकाधिक async XMLHttpRequest का उपयोग करें। जहां लौटाए गए प्रतिक्रियाओं का आदेश परिभाषित नहीं किया गया है (यह प्रतिक्रियाएं उसी क्रम में वापस नहीं आ सकती हैं जो वे भेजे गए थे)। यहां आउटपुट अन्य अनियंत्रित घटनाओं (सर्वर विलंबता इत्यादि) के अनुक्रम या समय पर निर्भर करता है। यह दौड़ की स्थिति संक्षेप में है।

तो एक भी इवेंट कतार (जैसे जावास्क्रिप्ट में) का उपयोग करने से भी अनियंत्रित क्रम में आने वाली घटनाओं को रोका नहीं जाता है और आपके कोड को इसका ख्याल रखना चाहिए।

+1

यह दौड़ की स्थिति नहीं है, यह बस एसिंक्रोनस कोड कैसे काम करता है। यदि आप बॉब को एक बॉक्स देते हैं और कहते हैं, "जब आप इस बॉक्स को खोलते हैं, तो उसे वापस सौंपें", फिर तुरंत मुकदमा चलाएं और उसे वही बात बताएं, जो आपको बॉक्स से पहले वापस ले जाती है? यह इस बात पर निर्भर है कि वे बॉक्स को खोलने में कितना समय लेते हैं, और आपके नियंत्रण से बाहर हैं। इसी तरह, एक AJAX अनुरोध उसी तरह काम करता है; आप यह नहीं मान सकते कि वे किसी भी विशिष्ट क्रम में पूरा हो जाएंगे क्योंकि आप उन्हें ऑर्डर नहीं कर रहे हैं, आप केवल "जब अनुरोध पूरा करते हैं, इस कॉलबैक फ़ंक्शन को चलाएं" कह रहे हैं। –

+3

रेस स्थिति थ्रेड के साथ नहीं होती है, यह केवल एक तरीका है कि आपके पास एक ही संसाधन के लिए प्रतिस्पर्धा करने वाले 2 या अधिक एसिंक फ़ंक्शन हैं और आप नहीं जानते कि कौन जीतेंगे और निष्पादन की अंतिम स्थिति क्या होगी। यही कारण है कि @Jens का वर्णन सही है –

-1

निश्चित रूप से आपको आवश्यकता है। यह हर समय होता है:

<button onClick=function() { 
    const el = document.getElementById("view"); 
    fetch('/some/api').then((data) => { 
    el.innerHTML = JSON.stringify(data); 
    }) 
}>Button 1</button> 

<button onClick=function() { 
    const el = document.getElementById("view"); 
    fetch('/some/other/api').then((data) => { 
    el.innerHTML = JSON.stringify(data); 
    }) 

}>Button 2</button> 

कुछ लोग इसे दौड़ की स्थिति के रूप में नहीं देखते हैं।

लेकिन यह वास्तव में है।

रेस स्थिति को व्यापक रूप से "इलेक्ट्रॉनिक, सॉफ़्टवेयर या अन्य सिस्टम का व्यवहार" कहा जाता है जहां आउटपुट अन्य अनियंत्रित घटनाओं के अनुक्रम या समय पर निर्भर होता है "।

यदि उपयोगकर्ता एक संक्षिप्त अवधि में इन 2 बटनों पर क्लिक करता है, तो आउटपुट को क्लिक करने के आदेश पर निर्भर होने की गारंटी नहीं है। यह इस बात पर निर्भर करता है कि कौन से एपीआई अनुरोध को जल्द ही हल किया जाएगा। इसके अलावा, आप जो डीओएम तत्व संदर्भित कर रहे हैं उसे किसी अन्य घटना (जैसे बदलते मार्ग) द्वारा हटाया जा सकता है।

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

इसे अपने प्रश्न में अनुकूलित करने के लिए, यह "ब्लॉक()" पर निर्भर करता है। यदि यह एक तुल्यकालिक कार्य है, तो आपको चिंता करने की आवश्यकता नहीं है। लेकिन अगर यह अतुल्यकालिक है, तो आपको चिंता करनी होगी:

function block() { 
    window.blockInProgress = true; 
    // some asynchronous code 
    return new Promise(/* window.blockInProgress = false */); 
    } 

    if(!window.blockInProgress) { 
    block(); 
    } else { 
    window.flashReady = block; 
    } 

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

एक और व्यावहारिक उदाहरण। विचार करें कि हम AJAX अनुरोधों को कैश करना चाहते हैं।

fetchCached(params) { 
    if(!dataInCache()) { 
    return fetch(params).then(data => putToCache(data)); 
    } else { 
    return getFromCache(); 
    } 
} 

तो ऐसा होता है यदि हम इस कोड को कई बार कॉल करते हैं? हम नहीं जानते कि कौन सा डेटा पहले वापस आएगा, इसलिए हम नहीं जानते कि कौन सा डेटा कैश किया जाएगा। पहले 2 बार यह ताजा डेटा वापस करेगा, लेकिन तीसरी बार हम वापस लौटने के जवाब के आकार को नहीं जानते हैं।

+0

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

+0

गलतफहमी यह उम्मीद कर रही है कि यदि बटन 1 क्लिक किया गया है और उसके बाद बटन 2 को शीघ्र ही बाद में क्लिक किया जाता है, तो पहली प्रतिक्रिया को दूसरे के बाद वापस किया जाना चाहिए। यह मुझे कलम पाने के लिए पहले व्यक्ति से पूछने से अलग नहीं है, फिर व्यक्ति को 2 बजे मुझे बाद में कलम प्राप्त करने के लिए कहें, और व्यक्ति 1 को पहले मुझे कलम प्राप्त करने की उम्मीद है। –

+0

चलो अपने उदाहरण में एक छोटा बदलाव करें। एक व्यक्ति के पास लाल कलम होता है, दूसरे व्यक्ति के पास नीली कलम होती है। आप इन दो लोगों से अपने डेस्क पर कलम लगाने के लिए कहते हैं, लेकिन यदि पहले से ही एक कलम है तो आप इसे बदलना चाहते हैं। – Vanuan

संबंधित मुद्दे