1. ホーム
  2. javascript

[解決済み] 非同期コールバック関数のセットを待機させるには?

2023-02-01 22:19:23

質問

javascriptでこのようなコードを持っています。

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

これらの非同期呼び出しがすべて終わった後、私はすべての配列の最小値を計算したいです。

どのように私はそれらすべてのために待つことができますか?

今のところ唯一のアイデアは、doneというブール値の配列を持っていて、i番目のコールバック関数でdone[i]をtrueに設定し、while(not all are done) {}と言うことです。

edit: 1つの可能な、しかし醜い解決策は、各コールバックでdone配列を編集し、そして各コールバックから他のすべてのdoneが設定された場合にメソッドを呼び出し、したがって完了する最後のコールバックは、継続するメソッドを呼び出すことだと思います。

事前にありがとうございます。

どのように解決するのですか?

コードがあまり具体的でないので、シナリオを作ってみます。 例えば、10回のajax呼び出しがあり、その10回のajax呼び出しの結果を蓄積し、それらがすべて完了したときに何かを行いたいとします。 このように、データを配列に蓄積して、最後の1つが終了したときに追跡することで実現できます。

手動カウンター

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呼び出しをどのように行うかによって異なるため、表示されません)。 1つの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標準プロミス

kbaの回答で指定されているように : ネイティブプロミスが組み込まれている環境(モダンブラウザやnode.js、babeljs transpileの使用やプロミスポリフィルの使用)であれば、ES6仕様のプロミスを使用することができます。 参照 この表 を参照してください。 プロミスは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
});


プロミスでない非同期処理をプロミスを返す処理にする必要がある場合、以下のように"promisify"することが可能です。

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