Soru Eşzamansız geri arama işlevleri için nasıl bekleyebilirim?


Javascript'te buna benzeyen bir kodum var:

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

Bu asenkron çağrıların tamamlanmasından sonra, tüm dizilerin üzerindeki min değerini hesaplamak istiyorum.

Hepsini nasıl bekleyebilirim?

Şu andaki tek fikrim, biten bir dizi boolene sahip olmak ve [i] işlevini geri arama işlevinde doğru olarak ayarlamaktır, sonra söyleyin (hepsi bitmez) {}

düzenleme: Bir olası, ancak çirkin çözüm, her geri aramada bitmiş diziyi düzenlemek, sonra tüm diğer geri sayım her geri aramadan ayarlanırsa bir yöntem çağırmak, böylece devam etmek için son geri çağrı devam eden yöntemi çağırmak olacağını varsayalım.

Şimdiden teşekkürler.


76
2018-04-04 02:14


Menşei


Async üzerinde Ajax isteğinin tamamlanmasını beklemek mi istiyorsunuz? - Peter Aron Zentai
Not, while (not all are done) { } işe yaramaz. Meşgul beklerken, geri aramalarınızdan hiçbiri işe yaramaz. - cHao
Evet. Geri arama yöntemlerini tetikleyecek şekilde geri dönmesi için harici bir API'ye bir async çağrısı bekliyorum. Evet, anladım ki, bu yüzden burada yardım istiyorum: D - codersarepeople
Bunu deneyebilirsin: github.com/caolan/async Çok güzel async yardımcı programı. - Paul Greyson


Cevaplar:


Kodunuzla çok spesifik olmadınız, bu yüzden bir senaryo hazırlayacağım. Diyelim ki, 10 ajax çağrınız var ve bu 10 ajax çağrısından elde edilen sonuçları biriktirmek istiyorsunuz ve sonra hepsi tamamlandığında bir şeyler yapmak istiyorsunuz. Verileri bir dizide biriktirerek ve sonuncusunu ne zaman bitirdiğini takip ederek bunu yapabilirsiniz:

Manuel Sayaç

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
        }
    });
}

Not: Hata işleme burada önemlidir (ajax çağrılarınızı nasıl yaptığınıza özel olduğu için gösterilmemiştir). Bir ajax çağrısı tamamlanmadığında, bir hatayla veya uzun bir süre ya da uzun bir süre sonra zaman aşımına uğradığında, durumu nasıl ele alacağınızı düşünmek isteyeceksiniz.


jQuery Promises

2014'te cevabımı ekliyorum. Bu günlerde, söz konusu sıkıntıları jQuery'nin $.ajax() zaten bir söz veriyor ve $.when() Bir grup sözün tamamı çözüldüğünde size bildirir ve sizin için dönüş sonuçlarını toplar:

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 Standart Sözler

Kba'nın cevabında belirtildiği gibi: yerleşik yerel vaatler (modern tarayıcı veya node.js veya babeljs transpile veya bir vaat polyfill kullanarak) ile bir ortam varsa, o zaman ES6 belirtilen sözler kullanabilirsiniz. Görmek bu masa tarayıcı desteği için. Sözler, IE dışındaki tüm güncel tarayıcılarda desteklenir.

Eğer doAjax() bir söz verir, sonra bunu yapabilirsiniz:

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
});

Söz vermeyen bir uyumsuzluk işlemini söz verdiğiniz birine iade etmeniz gerekiyorsa, bunu şöyle "promisify" edebilirsiniz:

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

Ve sonra yukarıdaki deseni kullanın:

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
});

Bluebird Vaatler

Eğer daha fazla özellik zengin bir kütüphane kullanıyorsanız Bluebird söz kütüphanesiDaha sonra bunu kolaylaştırmak için bazı ek işlevler vardır:

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

159
2018-04-04 02:19



JQuery çözümü benim için oldukça iyi çalışıyor! - larrydahooster
Kba - Özellikle tüm Ajax için jQuery kullanıyorsanız, tüm teknikler hala geçerli olduğundan, bu yanıtı modası geçmiş bir şey olmayacaktı. Ancak, yerel sözleri dahil etmek için çeşitli şekillerde güncelledim. - jfriend00
Bu günlerde, jquery'ye ihtiyaç duymayan daha temiz bir çözüm var. FetchAPI ve Promises ile yapıyorum - philx_x
@philx_x - IE ve Safari desteği hakkında ne yapıyorsunuz? - jfriend00
@ jfriend00 github bir polyfill yaptı github.com/github/fetch. Ya da babel'in henüz getirmeyi destekleyip desteklemediğinden emin değilim. babeljs.io - philx_x


2015'ten itibaren check-in yapıyoruz: yerli sözler içinde en yeni tarayıcı (Edge 12, Firefox 40, Chrome 43, Safari 8, Opera 32 ve Android tarayıcı 4.4.4 ve iOS Safari 8.4, ancak Internet Explorer, Opera Mini ve Android'in eski sürümleri).

Eğer 10 eşzamanlı eylem gerçekleştirmek istiyorsak ve hepsi bittiğinde bildirim alırsak, yerli kullanabiliriz Promise.allherhangi bir harici kütüphane olmaksızın:

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);
});

12
2017-11-19 20:29



Promises.all() olmalı Promise.all(). - jfriend00
Cevabınızın da başvurması gerekiyor hangi tarayıcılar kullanabilirsiniz Promise.all() içinde IE'nin güncel sürümlerini içermiyor. - jfriend00


JQuery’yi kullanabilirsiniz Vadeli nesne ile birlikte ne zaman yöntem.

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
});

10
2018-04-04 02:19



Soru etiketlenmedi jQuery hangi genellikle OP bir jQuery cevabı istemedi anlamına gelir. - jfriend00
@ jfriend00 jQuery'de zaten oluşturulduğunda tekerleği yeniden icat etmek istemedim - Paul
@Paul o zaman o kadar basit bir şey yapmak için junk 40kb önemsiz de dahil olmak üzere tekerleği yeniden icat (ertelendi) - Raynos
Ancak jQuery'yi ve buradaki özelini herkes kullanabilir veya kullanmak istemezse, sorunuzu jQuery ile etiketleyip etiketlemediğinizi belirtin. - jfriend00
$ .When çağrı bu örnek yanlıştır. Ertelenmiş / vaatler dizisini beklemek için $ .when.apply ($, promises) .then (function () {/ * do stuff * /}) kullanmanız gerekir. - danw


Bu şekilde öykünebilirsin:

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

sonra her bir async çağrısı yapar:

countDownLatch.count++;

her bir uyuşmazlıkta, yöntemin sonunda geri arama yaparken bu satırı eklersiniz:

countDownLatch.check();

Diğer bir deyişle, bir geri sayım-mandal işlevselliğini taklit edersiniz.


7
2018-04-04 02:21



Tüm kullanım örneklerinin% 99'unda bir Promise gitmenin yoludur, ancak bu cevabı beğendiğimden, bir Promise polyfill'in onu kullanan JS'den daha büyük olduğu durumlarda Async kodunu yönetmek için bir yöntem gösterilmektedir! - Sukima


Bu benim görüşüme göre en temiz yol.

Promise.all

FetchAPI

(bir sebepten dolayı Array.map benim için işe yaramıyor. benim için bu fonksiyonlar. Ama sen bir .forEach ve [] .concat () ya da benzer bir şey kullanabilirsiniz.

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);
})

3
2018-04-14 19:30



Bunun olması gerek. return responses.map(response => { return response.json(); })veya return responses.map(response => response.json()).


Gibi bir kontrol akışı kitaplığı kullanın 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)
})

1
2018-04-04 02:28