मैं अतुल्यकालिक कॉलबैक फ़ंक्शन के सेट का इंतजार कैसे कर सकता हूं?


95

मेरे पास ऐसा कोड है जो जावास्क्रिप्ट में कुछ इस तरह दिखता है:

forloop {
    //async call, returns an array to its callback
}

उन सभी के बाद एसिंक्स कॉल किया जाता है, मैं सभी सरणियों से मिनट की गणना करना चाहता हूं।

मैं उन सभी का इंतजार कैसे कर सकता हूं?

मेरा एकमात्र विचार अभी बुलियन का एक सरणी होना है, जिसे किया और सेट किया गया [i] को ith कॉलबैक फ़ंक्शन में सच करने के लिए, फिर कहते हैं (जबकि सभी नहीं किए गए हैं) {}

संपादित करें: मुझे लगता है कि एक संभव है, लेकिन बदसूरत समाधान, प्रत्येक कॉलबैक में किए गए सरणी को संपादित करना होगा, फिर एक विधि को कॉल करें यदि अन्य सभी किए गए प्रत्येक कॉलबैक से सेट होते हैं, इस प्रकार पूरा करने के लिए अंतिम कॉलबैक निरंतर विधि को कॉल करेगा।

अग्रिम में धन्यवाद।


1
Async पर क्या आपका मतलब है कि अजाक्स अनुरोध पूरा होने की प्रतीक्षा कर रहा है?
पीटर एरोन ज़ेंटई

6
ध्यान दें, while (not all are done) { }काम नहीं करेगा। जब आप व्यस्त-प्रतीक्षा कर रहे हों, तो आपका कोई भी कॉलबैक नहीं चल सकता है।
cHao

हाँ। मैं एक बाहरी एपीआई के लिए एक async कॉल का इंतजार कर रहा हूं ताकि यह कॉलबैक विधियों को आग लगा दे। हाँ cHao, मुझे एहसास हुआ कि, यही वजह है कि मैं यहाँ मदद के लिए पूछ रहा हूँ: D
codersarepeople

आप यह कोशिश कर सकते हैं: github.com/caolan/async Async उपयोगिता कार्यों का बहुत अच्छा सेट।
पॉल ग्रेसन

जवाबों:


191

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

मैनुअल काउंटर

var ajaxCallsRemaining = 10;
var returnedData = [];

for (var i = 0; i < 10; i++) {
    doAjax(whatever, function(response) {
        // success handler from the ajax call

        // save response
        returnedData.push(response);

        // see if we're done with the last ajax call
        --ajaxCallsRemaining;
        if (ajaxCallsRemaining <= 0) {
            // all data is here now
            // look through the returnedData and do whatever processing 
            // you want on it right here
        }
    });
}

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


jQuery के वादे

2014 में मेरे उत्तर को जोड़ते हुए। इन दिनों, वादे अक्सर इस प्रकार की समस्या को हल करने के लिए उपयोग किए जाते हैं क्योंकि jQuery के $.ajax()पहले से ही एक वादा वापस करता है और $.when()आपको बताएगा कि जब वादों का एक समूह सभी हल हो जाता है और आपके लिए वापसी परिणाम एकत्र करेगा:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push($.ajax(...));
}
$.when.apply($, promises).then(function() {
    // returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0]
    // you can process it here
}, function() {
    // error occurred
});

ES6 मानक वादे

जैसा कि केबीए के उत्तर में निर्दिष्ट किया गया है : यदि आपके पास अंतर्निहित वादों (आधुनिक ब्राउज़र या नोड.जेएस या बैबलज ट्रांसपाइल का उपयोग करके या एक वादा पॉलीफ़िल का उपयोग करके) के साथ एक वातावरण है, तो आप ईएस 6-निर्दिष्ट वादों का उपयोग कर सकते हैं। ब्राउज़र समर्थन के लिए यह तालिका देखें । IE को छोड़कर बहुत सारे वर्तमान ब्राउज़रों में वादों का समर्थन किया जाता है।

अगर doAjax()कोई वादा वापस करता है, तो आप ऐसा कर सकते हैं:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

यदि आपको वादे के मुताबिक एक गैर-वादा एस्क्वायस ऑपरेशन करना है, तो आप इसे इस तरह "प्रॉमिस" कर सकते हैं:

function doAjax(...) {
    return new Promise(function(resolve, reject) {
        someAsyncOperation(..., function(err, result) {
            if (err) return reject(err);
            resolve(result);
        });
    });
}

और, फिर ऊपर दिए गए पैटर्न का उपयोग करें:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

ब्लूबर्ड वादा करता है

यदि आप अधिक सुविधा संपन्न पुस्तकालय जैसे कि ब्लूबर्ड वादा पुस्तकालय का उपयोग करते हैं , तो इसे आसान बनाने के लिए कुछ अतिरिक्त कार्य किए गए हैं:

 var doAjax = Promise.promisify(someAsync);
 var someData = [...]
 Promise.map(someData, doAjax).then(function(results) {
     // all ajax results here
 }, function(err) {
     // some error here
 });

4
@kba - मैंने अभी तक इस उत्तर को पुराना नहीं कहा है क्योंकि सभी तकनीकें अभी भी लागू हैं, खासकर यदि आप पहले से ही अजाक्स के लिए jQuery का उपयोग कर रहे हैं। लेकिन, मैंने इसे देशी वादों को शामिल करने के लिए कई तरह से अपडेट किया है।
२२:०२

इन दिनों एक बहुत क्लीनर समाधान है जो भी jquery की जरूरत नहीं है। मैं इसे FetchAPI और वादे के साथ कर रहा हूं
फिल्पक्स_एक्स

@philx_x - आप IE और सफारी समर्थन के बारे में क्या कर रहे हैं?
jfriend00

@ jfriend00 github ने एक पॉलीफ़िल github.com/github/fetch बनाया । या मुझे यकीन नहीं है कि अगर बाबेल अभी तक समर्थन नहीं करता है। babeljs.io
दार्क्स_x

@philx_x - ऐसा सोचा। आजकल भ्रूण का उपयोग करने के लिए आपको एक पॉलीफिल लाइब्रेरी की आवश्यकता है। अजाक्स पुस्तकालय से बचने के बारे में आपकी टिप्पणी से थोड़ा हवा लेता है। फ़ेच अच्छा है, लेकिन यह पॉलीफ़िल के बिना इसका उपयोग करने में सक्षम होने से दूर है। यह अभी तक सभी ब्राउज़रों के नवीनतम संस्करण में भी नहीं है। करो, यह वास्तव में मेरे जवाब में कुछ भी नहीं बदलता है। मेरे पास एक doAjax()विकल्प के रूप में एक वादा लौटाया गया था । एक ही बात के रूप में fetch()
jfriend00

17

2015 से चेकिंग: अब हमारे पास सबसे हाल के ब्राउज़र में मूल वादे हैं (एज 12, फ़ायरफ़ॉक्स 40, क्रोम 43, सफारी 8, ओपेरा 32 और एंड्रॉइड ब्राउज़र 4.4.4 और आईओएस सफारी 8.4, लेकिन इंटरनेट एक्सप्लोरर नहीं, ओपेरा मिनी और पुराने संस्करण Android के)।

यदि हम 10 एसिंक्स क्रिया करना चाहते हैं और सभी समाप्त होने पर अधिसूचित हो जाते हैं, तो हम Promise.allबिना किसी बाहरी पुस्तकालयों के मूल का उपयोग कर सकते हैं :

function asyncAction(i) {
    return new Promise(function(resolve, reject) {
        var result = calculateResult();
        if (result.hasError()) {
            return reject(result.error);
        }
        return resolve(result);
    });
}

var promises = [];
for (var i=0; i < 10; i++) {
    promises.push(asyncAction(i));
}

Promise.all(promises).then(function AcceptHandler(results) {
    handleResults(results),
}, function ErrorHandler(error) {
    handleError(error);
});

2
Promises.all()होना चाहिए Promise.all()
२२

1
आपके उत्तर को यह भी बताना होगा कि आप किन ब्राउज़रों का उपयोग कर सकते हैं Promise.all()जिनमें IE का कोई वर्तमान संस्करण शामिल नहीं है।
23

10

जब विधि के साथ आप jQuery के आस्थगित ऑब्जेक्ट का उपयोग कर सकते हैं ।

deferredArray = [];
forloop {
    deferred = new $.Deferred();
    ajaxCall(function() {
      deferred.resolve();
    }
    deferredArray.push(deferred);
}

$.when(deferredArray, function() {
  //this code is called after all the ajax calls are done
});

7
प्रश्न को टैग नहीं किया गया था jQueryजिसका अर्थ है आमतौर पर ओपी को jQuery का जवाब नहीं चाहिए था।
jfriend00

8
@ jfriend00 जब मैं पहले से ही jQuery में बनाया गया था पहिया को सुदृढ़ नहीं करना चाहते थे
पॉल

4
@Paul तो बल्कि फिर से 40kb सहित पहिया को फिर से आविष्कार करें ताकि कुछ सरल (deferreds) कर
सकें

2
लेकिन हर कोई jQuery का उपयोग नहीं कर सकता है या नहीं चाहता है और SO पर यहां का रिवाज यह है कि आप इंगित करते हैं कि आप अपने प्रश्न को jQuery से टैग करते हैं या नहीं।
jfriend00

4
$ .When कॉल यह उदाहरण गलत है। आस्थगित / वादों की एक सरणी के लिए प्रतीक्षा करने के लिए आपको $ .when.apply ($, वादे) .then (फ़ंक्शन () {/ * do stuff * /}) का उपयोग करने की आवश्यकता है।
danw

9

आप इसका इस तरह अनुकरण कर सकते हैं:

  countDownLatch = {
     count: 0,
     check: function() {
         this.count--;
         if (this.count == 0) this.calculate();
     },
     calculate: function() {...}
  };

फिर प्रत्येक async कॉल यह करता है:

countDownLatch.count++;

प्रत्येक asynch में जब आप इस लाइन को जोड़ने की विधि के अंत में कॉल करते हैं:

countDownLatch.check();

दूसरे शब्दों में, आप एक गिनती-डाउन-लैच कार्यक्षमता का अनुकरण करते हैं।


सभी उपयोग के 99% मामलों में एक वादा जाने का तरीका है, लेकिन मुझे यह उत्तर पसंद है क्योंकि यह उन स्थितियों में Async कोड को प्रबंधित करने के लिए एक विधि दिखाता है जहां एक वादा पॉलीफ़िल बड़ा है तो JS इसका उपयोग करता है!
सुकीमा

6

यह मेरी राय में सबसे साफ तरीका है।

Promise.all

FetchAPI

(किसी कारण से Array.map मेरे अंदर काम नहीं करता है। मेरे लिए कार्य करता है। लेकिन आप एक .forEach और [] .concat () या कुछ इसी तरह का उपयोग कर सकते हैं।

Promise.all([
  fetch('/user/4'),
  fetch('/user/5'),
  fetch('/user/6'),
  fetch('/user/7'),
  fetch('/user/8')
]).then(responses => {
  return responses.map(response => {response.json()})
}).then((values) => {
  console.log(values);
})

1
मुझे लगता है कि यह होना चाहिए return responses.map(response => { return response.json(); }), या return responses.map(response => response.json())

1

जैसे एक नियंत्रण प्रवाह पुस्तकालय का उपयोग करें after

after.map(array, function (value, done) {
    // do something async
    setTimeout(function () {
        // do something with the value
        done(null, value * 2)
    }, 10)
}, function (err, mappedArray) {
    // all done, continue here
    console.log(mappedArray)
})
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.