2012-07-14 22 views
61

मैं निम्न रूप में घटना पाश चला रहा हूँ के लिए एक जावास्क्रिप्ट अंदर प्रक्रिया:अतुल्यकालिक पाश

var i; 
var j = 10; 
for (i = 0; i < j; i++) { 

    asycronouseProcess(callBackFunction() { 
     alert(i); 
    }); 
} 

मैं इस चाहते हैं क्या प्रदर्शित करने के लिए 10 के माध्यम से संख्या 0 दिखा अलर्ट की एक श्रृंखला समस्या यह है कि है जब तक कॉल बैक फ़ंक्शन ट्रिगर होता है तब तक लूप पहले से ही कुछ पुनरावृत्तियों से गुजर चुका है और यह मेरे उच्च मूल्य को प्रदर्शित करता है। इसे ठीक करने के तरीके पर कोई सिफारिश?

+0

'पैरामीटर को' asynchronousProcess' फ़ंक्शन में जोड़ने के बारे में कैसे? जो इसे कॉलबैक पर पास कर सकता है फंक्शन –

+1

संभावित डुप्लिकेट [जावास्क्रिप्ट बंद लूप के अंदर बंद करना - सरल व्यावहारिक उदाहरण] (http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) –

उत्तर

114

for पाश तुरंत पूरा होने के लिए चलाता है जबकि आपके सभी एसिंक्रोनस ऑपरेशन शुरू हो जाते हैं। जब वे भविष्य में कुछ समय पूरा करते हैं और कॉलबैक कहते हैं, तो आपके लूप इंडेक्स वैरिएबल i का मूल्य सभी कॉलबैक के लिए अंतिम मूल्य होगा।

ऐसा इसलिए है क्योंकि for लूप लूप के अगले पुनरावृत्ति पर जारी रखने से पहले एक एसिंक्रोनस ऑपरेशन को पूरा करने की प्रतीक्षा नहीं करता है और क्योंकि एसिंक कॉलबैक को भविष्य में कुछ समय कहा जाता है। इस प्रकार, लूप इसके पुनरावृत्तियों को पूरा करता है और फिर उन एसिंक ऑपरेशन समाप्त होने पर कॉलबैक को कॉल किया जाता है। इस प्रकार, लूप इंडेक्स "किया गया" है और सभी कॉलबैक के लिए इसके अंतिम मूल्य पर बैठा है।

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

2016 के रूप में, यदि आप जावास्क्रिप्ट की एक पूरी तरह से अप-टू-कल्पना ES6 कार्यान्वयन है, तो आप भी let उपयोग कर सकते हैं for पाश चर को परिभाषित करने के लिए और इसे विशिष्ट (for पाश से प्रत्येक यात्रा तीसरे कार्यान्वयन के लिए परिभाषित किया जाएगा नीचे)। लेकिन, ध्यान दें कि यह ES6 कार्यान्वयन में देर से कार्यान्वयन सुविधा है, इसलिए आपको यह सुनिश्चित करना होगा कि आपका निष्पादन वातावरण उस विकल्प का समर्थन करता है।

उपयोग करें।foreach() पुनरावृति है, क्योंकि यह बनाता है अपनी ही समारोह बंद

someArray.forEach(function(item, i) { 
    asynchronousProcess(function(item) { 
     console.log(i); 
    }); 
}); 

अपनी खुद समारोह बंद बनाएँ Iife का उपयोग

var j = 10; 
for (var i = 0; i < j; i++) { 
    (function(cntr) { 
     // here the value of i was passed into as the argument cntr 
     // and will be captured in this function closure so each 
     // iteration of the loop can have it's own value 
     asynchronousProcess(function() { 
      console.log(cntr); 
     }); 
    })(i); 
} 

या बाहरी समारोह संशोधित करें और यह चर

दर्रा

यदि आप asynchronousProcess() फ़ंक्शन को संशोधित कर सकते हैं, तो आप वहां केवल मान पास कर सकते हैं और asynchronousProcess() इस तरह कॉलबैक करने के लिए वापस CNTR काम करते हैं:

var j = 10; 
for (var i = 0; i < j; i++) { 
    asynchronousProcess(i, function(cntr) { 
     console.log(cntr); 
    }); 
} 

उपयोग ES6 let

आप Javascript निष्पादन वातावरण है कि पूरी तरह से समर्थन करता है ES6 है, तो आप let अपने for पाश इस तरह में उपयोग कर सकते हैं:

const j = 10; 
for (let i = 0; i < j; i++) { 
    asynchronousProcess(function() { 
     console.log(i); 
    }); 
} 

let एक for पाश घोषणा में घोषित की तरह इस एक uni पैदा करेगा लूप के प्रत्येक आमंत्रण के लिए i का que मान (जो आप चाहते हैं)। अपने async समारोह एक वादा देता है, तो

वादों और async साथ Serializing/इंतजार

, और आप के बजाय समानांतर में की एक के बाद एक को चलाने के लिए अपने async संचालन क्रमानुसार करने चाहते हैं और आप एक आधुनिक में चला रहे हैं पर्यावरण जो async और await का समर्थन करता है, तो आपके पास और विकल्प हैं।

async function someFunction() { 
    const j = 10; 
    for (let i = 0; i < j; i++) { 
     // wait for the promise to resolve before advancing the for loop 
     await asynchronousProcess(); 
     console.log(i); 
    } 
} 

यह सुनिश्चित करें कि asynchronousProcess() को केवल एक कॉल एक समय में उड़ान में है और for पाश भी जब तक हर एक से किया जाता है अग्रिम नहीं होगा कर देगा। यह पिछली योजनाओं से अलग है जो सभी ने आपके असीमित परिचालन को समानांतर में चलाया है, इसलिए यह पूरी तरह से निर्भर करता है कि आप किस डिज़ाइन को चाहते हैं। नोट: await एक वादे के साथ काम करता है ताकि आपके फ़ंक्शन को एक ऐसा वादा वापस कर दिया जाए जिसे एसिंक्रोनस ऑपरेशन पूरा होने पर हल/अस्वीकार कर दिया गया हो। साथ ही, ध्यान दें कि await का उपयोग करने के लिए, युक्त फ़ंक्शन को async घोषित किया जाना चाहिए।

+1

यदि आप 'asycronouseProcess()' फ़ंक्शन को संशोधित कर सकते हैं तो दूसरा विकल्प जोड़ा गया। – jfriend00

+0

बहुत बढ़िया! इससे मुझे कैस्परजेएस के साथ बहुत मदद मिली। – Manu

+0

ES6 'let' कार्यान्वयन जोड़ा गया। – jfriend00

0

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

समाधान आपको वास्तव में आवश्यक चीज़ों पर निर्भर करता है। यदि उदाहरण आपको बिल्कुल वही चीज़ के करीब है, तो आपके एसिंक प्रक्रिया में i पास करने के लिए साइमन का सुझाव एक अच्छा है।

9

इसे ठीक करने के तरीके पर कोई अनुशंसा?

कई।आप bind उपयोग कर सकते हैं:

for (i = 0; i < j; i++) { 
    asycronouseProcess(function (i) { 
     alert(i); 
    }.bind(null, i)); 
} 

या, अपने ब्राउज़र का समर्थन करता है, तो let (यह अगले ECMAScript संस्करण में होगा, हालांकि फ़ायरफ़ॉक्स पहले से ही यह कुछ समय के बाद से समर्थन करता है) आप हो सकता है:

for (i = 0; i < j; i++) { 
    let k = i; 
    asycronouseProcess(function() { 
     alert(k); 
    }); 
} 

या , तो आप मैन्युअल bind का काम कर सकता है (मामले में ब्राउज़र इसका समर्थन नहीं करता है, लेकिन मैं आपको लगता है कि मामले में एक शिम लागू कर सकते हैं कहेंगे, यह कड़ी में ऊपर होना चाहिए):

for (i = 0; i < j; i++) { 
    asycronouseProcess(function(i) { 
     return function() { 
      alert(i) 
     } 
    }(i)); 
} 

मैं आमतौर पर let पसंद करता हूं जब मैं इसका उपयोग कर सकता हूं (उदा। फ़ायरफ़ॉक्स ऐड-ऑन के लिए); अन्यथा bind या एक कस्टम currying फ़ंक्शन (जिसे संदर्भ ऑब्जेक्ट की आवश्यकता नहीं है)।

+0

ईसीएमएस्क्रिप्ट उदाहरण यह दिखाने के लिए एक बहुत अच्छा है कि 'चलो' क्या कर सकता है। – hazelnut

+0

क्या किसी भी प्रकार के प्लेसहोल्डर के सभी उत्तरों में 'asyncronouseProcess' है? मुझे "परिभाषित नहीं किया जा रहा है"। – JackHasaKeyboard

+0

'asyncronouseProcess' मूल प्रश्न का हिस्सा है, इसलिए हाँ, यह सामान्य है अगर यह आपको "परिभाषित नहीं" देता है। यदि आप मूल समस्या की जांच करना चाहते हैं और प्रस्तावित समाधान कैसे काम करता है तो आप इसे किसी भी एसिंक फ़ंक्शन के साथ प्रतिस्थापित कर सकते हैं। उदाहरण के लिए: 'कार्य asycronouseProcess (fn) {setTimeout (fn, 100);} ' – ZER0

5

async await यहां (ES7) है, इसलिए आप इस तरह की चीजें अब आसानी से कर सकते हैं।

var i; 
    var j = 10; 
    for (i = 0; i < j; i++) { 
    await asycronouseProcess(); 
    alert(i); 
    } 

याद रखें, यह काम करता है एक Promise

तो asycronouseProcess अपने नियंत्रण में नहीं है केवल अगर asycronouseProcess लौटा रहा है तो आप इसे एक Promise लौट इस

function asyncProcess() { 
    return new Promise((resolve, reject) => { 
    asycronouseProcess(()=>{ 
     resolve(); 
    }) 
    }) 
} 

की तरह खुद ही कर सकते हैं फिर इस लाइन को await asycronouseProcess();await asyncProcess();

द्वारा प्रतिस्थापित करें

Promises को समझना से पहले भी async await में देख चाहिए (इसके अलावा async await के लिए समर्थन के बारे में पढ़ा)

0

var i = 0; 
 
var length = 10; 
 

 
function for1() { 
 
    console.log(i); 
 
    for2(); 
 
} 
 

 
function for2() { 
 
    if (i == length) { 
 
    return false; 
 
    } 
 
    setTimeout(function() { 
 
    i++; 
 
    for1(); 
 
    }, 500); 
 
} 
 
for1();

है यहाँ यहाँ क्या उम्मीद है करने के लिए एक नमूना कार्यात्मक दृष्टिकोण है।