【问题标题】:Node.js - Wait for multiple async calls to finish before continuing in codeNode.js - 在继续代码之前等待多个异步调用完成
【发布时间】:2018-11-28 04:55:59
【问题描述】:

所以基本上我有一个带有异步函数的 for 循环。问题是程序在循环之后继续,我希望它等到在循环中调用的所有异步函数都完成后,代码才能继续。

在我的代码中,“bar”是一个 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;
});

【问题讨论】:

  • 查看 Promise。
  • 在这种情况下,您不能使用同步 for 循环。请参考 npm 中的库 async。它的许多功能都可以根据您的用例使用
  • 我已更新 my answer 以编辑 for(var foo in bar) 循环。

标签: javascript node.js loops asynchronous npm


【解决方案1】:

这里最简单的方法是直接或通过async/await 语法使用promise。在这种情况下,可能是直接的。

首先,您必须让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]));
  });
}

请注意,在该循环中为foo 使用constlet 而不是var 很重要,因为then 回调会关闭它;请参阅 this question's answers 了解为什么 constlet 可以做到这一点。

每次调用write 只会在前一个工作完成后进行。

然后等待整个过程完成:

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

您没有使用来自write 的请求的布尔值显示任何内容,但它们可以(作为数组)作为来自write 的承诺的分辨率值。


过程的第二部分,我们调用write,也可以写在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
});

...或者如果这是 alsoasync 函数中:

await doTheWrites();

【讨论】:

  • 嗯,现在我在 asyncFunctionCall() 处遇到错误 --> “错误:未定义不是有效的 uri 或选项对象。”
  • @Rxchard - 这与上述更改无关,听起来像是将url 传递给它的问题。
  • 好的,现在好像可以了。非常感谢 T.J. :)
  • 更好的是,您可以使用const 而不是let,因为foo 不会被重新分配。
  • @ScottRudiger - 非常正确。我一直用for-infor-of 来做这件事,我想知道为什么我在上面写了let。嗯。 :-)
【解决方案2】:

使函数异步并等待调用:

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

【讨论】:

  • 还有一个小细节,据我们所知,asyncFunctionCall 没有返回承诺,因为它使用了request 和旧式 Node.js 回调。
猜你喜欢
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
相关资源
最近更新 更多