2016-07-17 7 views
1

私はループ内で複数の(並列の)非同期呼び出しを行う関数fooを持っています。私は何とかすべての呼び出しの結果が利用可能になるまで待つ必要があります。 fooの完全な結果を返すにはどうすればいいですか?すべてのデータが利用可能な場合は、後で何らかの処理を開始するにはどうすればよいですか?複数の(並列の)非同期関数呼び出しの累積結果をループ内で返すにはどうすればよいですか?

各結果を配列に追加しようとしましたが、配列を使用する必要があるポイントの後に配置されません。

function foo() { 
    var results = []; 

    for (var i = 0; i < 10; i++) { 
     someAsyncFunction({someParam:i}, function callback(data) { 
     results.push(data); 
     }); 
    } 
    return results; 
} 

var result = foo(); // It always ends up being an empty array at this point. 

注:この問題は、既存の汎用"How do I return the response from an asynchronous call?" questionの線に沿って、意図的に汎用的です。この質問には優れた回答がいくつかありますが、複数の非同期呼び出しをカバーしていません。複数の呼び出しについて言及する他のいくつかの質問がありますが、ループベースのものを見つけることができず、jQueryの回答しかないものもあります。ここでは、特定のライブラリに依存しない一般的なテクニックが必要です。

+1

ああ、私は洙長い:-) – Bergi

+0

ない我々は並列動作をカバーしたい場合は必ず、一連の動作またはその両方(I」のために、この上の正規のを望んでいましたdではなく別々の質問を参照)。これは質問に明確に記述してください(そして可能性のあるタイトルもあります) – Bergi

+0

実装の詳細を見たい場合。参照:http://stackoverflow.com/questions/4631774/coordinating-parallel-execution-in-node-js/4631909#4631909その答えはずっと前にasync.jsの前に書かれていました。コードを書く場合は、async.jsを使用してください。あなたが約束を使用してコールバックの配列を構築し、 'promise.all'を使用することを好む場合 – slebetman

答えて

8

約束を使用してください。正確にはPromise.allがこのために設計されています。

約束の配列(または反復可能)をとり、配列のすべての約束が解決されたときに解決される新しい約束を返します。それ以外の場合は、配列の約束が拒否されたときに拒否されます。

function someAsyncFunction(data, resolve, reject) { 
 
    setTimeout(function() { 
 
    if(Math.random() < .05) { 
 
     // Suppose something failed 
 
     reject('Error while processing ' + data.someParam); 
 
    } else { 
 
     // Suppose the current async work completed succesfully 
 
     resolve(data.someParam); 
 
    } 
 
    }, Math.random() * 1000); 
 
} 
 

 
function foo() { 
 
    
 
    // Create an array of promises 
 
    var promises = []; 
 
    
 
    for (var i = 0; i < 10; i++) { 
 
    // Fill the array with promises which initiate some async work 
 
    promises.push(new Promise(function(resolve, reject) { 
 
     someAsyncFunction({someParam:i}, resolve, reject); 
 
    })); 
 
    } 
 
    
 
    // Return a Promise.all promise of the array 
 
    return Promise.all(promises); 
 
} 
 

 
var result = foo().then(function(results) { 
 
    console.log('All async calls completed successfully:'); 
 
    console.log(' --> ', JSON.stringify(results)); 
 
}, function(reason) { 
 
    console.log('Some async call failed:'); 
 
    console.log(' --> ', reason); 
 
});

結果は約束をして解決された順序で約束、ないの配列の順序に従って与えられることに注意してください。

2

行うための簡単な方法から

function foo(cb) { 
    var results = []; 

    for (var i = 0; i < 10; i++) { 
     someAsyncFunction({someParam:i}, function callback(data) { 
     results.push(data); 

     if(results.length===10){ 
      cb(results); 
     } 
     }); 
    } 

} 

foo(function(resultArr){ 
    // do whatever with array of results 
}); 

だけの違い:それは一度、すべての応答がアレイに含まれるコールバックをトリガするだろうアプローチは結果の順序は保証されていません。それはいくつかの追加で簡単に達成できます。

+0

結果の順序は少し注意して保証することができます。参照:http://stackoverflow.com/questions/4631774/coordinating-parallel-execution-in-node-js/4631909#4631909 – slebetman

2

私はずっと前に非常によく似た質問に答えました:Coordinating parallel execution in node.js

しかし、時間が移動しました。それ以来、本当に良い図書館が登場しており、約束のデザインパターンは完全に探究され、さらには標準化されています。あなたが生のコードでそれを行う方法を見たい場合は上記のリンクをクリックしてください。あなただけに読み取ったコードにしたい場合は...

async.js

async.js libraryは、基本的には上記のリンクのコードを実装しています。 asyncでは、書くコードは次のようになります。

var listOfAsyncFunctions = []; 

for (var i = 0; i < 10; i++) { 
    (function(n){ 
     // Construct an array of async functions with the expected 
     // function signature (one argument that is the callback). 
     listOfAsyncFunctions.push(function(callback){ 
      // Note: async expects the first argument to callback to be an error 
      someAsyncFunction({someParam:n}, function (data) { 
       callback(null,data); 
      }); 
     }) 
    })(i); // IIFE to break the closure 
} 

// Note that at this point you haven't called the async functions. 
// Pass the array to async.js and let it call them. 

async.parallel(listOfAsyncFunctions,function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

しかし、async.jの作者はそれ以上のことをしています。非同期にも機能的な配列のような操作があります。それぞれ、map、filter、reduce。これは、非同期処理のアレイが簡単になり、コードが容易に理解することができます:

var listOfParams = []; 

for (var i = 0; i < 10; i++) { 
    // Construct an array of params: 
    listOfParams.push({someParam:i}); 
} 

async.map(listOfParams,someAsyncFunction,function (err,result) { 
    console.log(result); 
}); 

もう一つの非同期は、あなたが非同期タスクを処理する方法のための異なるアルゴリズムで提供します。たとえば、ウェブサイトを傷つけたいけど、自分のサーバーにスパムを送信するためにあなたのIPアドレスを禁止したくないと言ってください。一度タスクを1つずつ処理するasync.series()の代わりparallelを使用することができます。

async. parallelLimit(listOfAsyncFunctions, 3, function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

Promise.all()

// Set-up listOfAsyncFunctions as above 

async.series(listOfAsyncFunctions,function (err,result) { 
    console.log(result); // result will be the same order as listOfAsyncFunctions 
}); 

それとも、一度に3つのタスクを処理する場合

Promise.all()メソッドは、async.parallel()と同様に動作しますが、代わりに約束で動作します。あなたはその後、Promise.all()に渡す約束の配列を作成:

var listOfPromises = []; 

for (var i = 0; i < 10; i++) { 
    // Construct an array of promises 
    listOfPromises.push(somePromiseFunction({someParam:i})); 
} 

Promise.all(listOfPromises).then(function(result){ 
    console.log(result); 
}); 
関連する問題