web-dev-qa-db-ja.com

Node.js-コードを続行する前に、複数の非同期呼び出しが完了するのを待ちます

基本的に、非同期関数を含むforループがあります。問題は、プログラムがループの後に続くだけで、コードが続行する前にループで呼び出されたすべての非同期関数が終了するまで待機することです。

私のコードでは、「バー」は他のjson配列を含むjson配列です。

function write(bla) { // gets called one after another

  for(var url in bla) {
    asyncFunctionCall(url); // Executed about 50 times, it has to run parallel
  }
  // Wait for all called functions to finish before next stuff happens and
  // write gets called again.

}

for(var foo in bar) {
  // Here i parse the json array "foo" which is in the json array "bar"
  write(foo[bla]); // bla is an array of multiple urls.
}

非同期関数呼び出しは次のようになります。

var request = require('request');

request(url, function (error, response, body) {
  if(typeof response !== 'undefined') {
    if((response.statusCode >= 400 && response.statusCode <= 451)
    || (response.statusCode >= 500 && response.statusCode <= 511))
      return true;
    return false;
  }
  return false;
});
8
Rxchard

ここで最も簡単な方法は、直接またはasync/await構文を介してプロミスを使用することです。この場合、おそらく直接。

まず、asyncFunctionCallがプロミスを返すようにする必要があります。常にブール値を返すように見えるので、この場合は常にプロミスを解決します:

function asyncFunctionCall(url) {
  return new Promise(resolve => {
    request(url, function (error, response, body) {
      if(typeof response !== 'undefined') {
        if((response.statusCode >= 400 && response.statusCode <= 451)
        || (response.statusCode >= 500 && response.statusCode <= 511)) {
          resolve(true);
          return;
        }
      }
      resolve(false);
    });
  });
}

次に、約束の配列を作成し、Promise.allを使用して、すべての約束が完了するのを待ちます。これらの呼び出しは、並行して実行します

function write(bla) { // gets called one after another
  const promises = [];
  for(var url in bla) {
    promises.Push(asyncFunctionCall(url)); // Executed about 50 times.
  }
  return Promise.all(promises);
}

次に、writeからすべてのプロミスのチェーンを構築して、それらが連続して実行されるようにします。

let p = Promise.resolve();
for (const foo in bar) { // <== Notice `const`
  // See "Edit" below
  p = p.then(() => {
    // Here i parse the json object "foo" in the json array "bar"
    // bla is an array of multiple urls.
    return write(foo[bla]));
  });
}

constコールバックが終了するため、そのループでletではなくvarではなくfooまたはthenを使用することが重要であることに注意してください。 この質問の回答 を参照してくださいconstおよびletが機能する理由。

writeへの各呼び出しは、前の作業が完了したときにのみ行われます。

次に、プロセス全体が完了するのを待ちます。

p.then(() => {
  // All done
});

何も表示していませんsingwriteのリクエストからのブール値ですが、writeからのプロミスの解決値として(配列として)利用可能です。


writeを呼び出すプロセスの2番目の部分は、論理フローをより明確にするasync関数で記述することもできます。

async function doTheWrites() {
  for (const foo in bar) {
    // Here i parse the json object "foo" in the json array "bar"
    // bla is an array of multiple urls.
    await write(foo[bla]);
  }
}

次に、全体的なプロセス:

doTheWrites().then(() => {
  // All done
});

...またはこれがasync関数でalsoの場合:

await doTheWrites();
9
T.J. Crowder

関数を非同期にし、呼び出しを待機します。

async function write(foo) {
   for(const url of foo) {
      await asyncFunctionCall(url);
   }  
}

(async function() {
   for(const foo of bar) {
     await  write(foo);
   }
})()

それは次々にリクエストを実行します。それらを並行して実行するには、Promise.allを使用します。

const write = foo => Promise.all(foo.map(asyncFunctionCall));

Promise.all(bar.map(write))
  .then(() => console.log("all done"));
2
Jonas Wilms