NodeJS:在迭代之间等待的异步循环

时间:2019-07-02 11:42:36

标签: javascript node.js

在将一组对象(objects [])保存到数据库(使用mongoose的mongoDB)之前,我正在尝试进行一些检查:

  • 这些对象已经按日期排序,因此object [0] .date低于objects [1] .date。
  • 每个对象都应检查最后一个相关的已保存对象具有不同的值(以避免两次保存相同的信息)。这意味着我必须在每次保存之前查询数据库,以进行检查,并且必须存储每个对象,以便能够使用正确的对象进行检查。如果未按顺序存储对象,则上一个相关的已保存对象可能不是正确的对象。

深入的解释:

HTTP请求发送到API。它返回我要处理的对象数组(按日期排序),并保存在Mongo DB中(使用mongoose)。我必须遍历所有这些对象,对于每个对象:

  • 查找存储在DB上的先前相关对象(该对象可能是该数组之一)。
  • 检查“已存储”和要保存的对象之间的一些值,以评估是否必须保存或可以丢弃新对象。
  • 保存或丢弃它,然后跳至下一个迭代。

等待每次迭代完成很重要,因为:

  • 数组中的项目必须按顺序存储在数据库中:首先是日期较低的项目,因为每个项目都可以被后来存储日期较高的某些对象修改。
  • 如果下一个迭代在上一个对象完成之前开始,则搜索前一个对象的查询如果尚未存储则找不到它

已尝试:

在forEach / for循环上使用promise或async / await只会使该迭代异步,但会保持一次启动所有迭代。

我尝试在forEach / for循环中使用async / await函数,甚至创建了自己的asyncForEach函数(如下所示),但是这些都没有起作用:

Array.prototype.asyncForEach = function(fn) {
  return this.reduce(
   (promise, n) => promise.then(() => fn(n)),
  Promise.resolve()
 );
};

测试功能:

let testArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

testArray.asyncForEach(function(element) {
  setTimeout(() => {
    console.log(element);
  }, Math.random() * 500);
});

提供的示例应在每种情况下按顺序显示数字。内部函数(在示例中为setTimeout)应该返回promise没问题。

我认为我需要的是一个循环,该循环在迭代之间等待一些功能/承诺,并且仅在第一个迭代已经完成时才开始下一个迭代。

我该怎么做?预先谢谢你!

3 个答案:

答案 0 :(得分:3)

const myArray = ['a','b','c','d'];

async function wait(ms) { // comment 3
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function doSomething() {
  await myArray.reduce(async (promise, item) => {
    await promise; // comment 2
    await wait(1000);

    // here we could await something else that is async like DB call
    document.getElementById('results').append(`${item} `);
  }, Promise.resolve()); // comment 1
}


setTimeout(() => doSomething(), 1000);
<div id="results">Starting in 1 second <br/></div>

您也可以使用已经说过的reduceasync await

基本上,如果您了解reduce的工作原理,您会看到它接受2个参数,第一个是对每个步骤执行的回调,第二个是可选的初始值。

在回调中,我们的第一个参数是累加器,这意味着它接受上一步返回的内容或第一步的可选初始值。

1)您正在提供承诺解决方案的初始价值,以便您开始第一步。

2)由于这个await promise,您将永远不会进行下一步,直到上一步完成为止,因为这是上一步的累加器值,这是有保证的,因为我们说回调是async 。我们在这里并没有解决每个人说的诺言,但是一旦上一步完成,我们将隐式解决它,然后继续下一步。

3)您可以放置​​await wait(30)例如,以确保您正在限制Ajax请求并且没有发送太多请求给第三方API,因为那样的话您发送的消息数不会超过1000 /即使您的代码在计算机上执行得非常快,每秒也可以处理30个请求。

答案 1 :(得分:1)

嗯,好的,我不确定我是否以正确的方式理解了您的问题。但是,如果您尝试执行等待每个项目逻辑的异步数组操作,则可以执行以下操作:

async loadAllUsers() {
    const test = [1,2,3,4];
    const users = [];
    for (const index in test) {
      // make some magic or transform data or something else
      await users.push(test[index])
    }

    return users;
  }

然后,您可以使用“ await”简单地调用此函数。希望对您有所帮助。

答案 2 :(得分:1)

asyncForEach函数中您要解决一个Promise,setTimeout不会返回Promise。因此,如果您将setTimeout转换为Promise。它将按预期工作。 这是修改后的代码:

testArray.asyncForEach(function(element) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   console.log(element);
   return resolve(element)
 }, Math.random() * 500);
})
});