繁体   English   中英

nodejs使用异步调用循环遍历数组?

[英]nodejs looping through array with async calls?

我试图遍历一个数组,它将一个new Thing推送到一个列表,在Thing内部它会做一些自己的异步调用。 我将如何以同步方式迭代数组,因为callback需要列表中的数据才能工作。 由于我的for循环是同步的并且进行了一些异步调用,因此如果完成了调用,则会在列表之前调用回调。

我不知道如何迭代数组并在进行回调之前完成所有工作

load(file, callback) {
  fs.readFile(file, (err, fd) => {
    var data = JSON.parse(fd);

    for(var i of data.array){
      this.list.push(new Thing(i.id)); // new Thing is Asynchronous
    }
    callback(); // Needs a finished list
    return;
  });
}

解决了它:

通过将我的Thing类转换为同步,通过删除对类中的函数的异步调用,并首先实例化循环中的所有Things然后调用Promise.all调用函数我解决了问题:

load(file, callback) {
  fs.readFile(file, (err, fd) => {
    var data = JSON.parse(fd);

    for(var i of data.array){
      this.list.push(new Thing(i.id));
    }

    Promise.all(this.list.map(i => i.load()).then(callback);
  });
}

你必须在Thing内部有一些状态来跟踪它的完成情况,例如你可以拥有一个承诺的实例变量。 因此,鉴于该黑客在一起例子Thing

class Thing {
  constructor(id) {
    this.id = id;
    this.done = new Promise((resolve, reject) => {
      asyncThingWithCallback((err, data) {
        if (err) {
          this.asyncVal = null;
          reject(err);
        } else {
          this.asyncVal = data;
          resolve()
        }
      })
    });
  }
}

您可以在回调中使用done属性,如下所示:

load(file, callback) {
  fs.readFile(file, (err, fd) => {
    var data = JSON.parse(fd);

    for(var i of data.array){
      this.list.push(new Thing(i.id)); // new Thing is Asynchronous
    }

    Promise.all(this.list.map((thing) => thing.done))
      .then(callback)
  });
}

首先,通常不建议使用需要一些异步操作来完成创建有效对象的构造函数。 这不会导致易于编写或维护的代码,因为构造函数必须返回对象引用,并且因为操作是异步的,所以它必须在创建有效对象之前返回该对象引用。 这只会导致凌乱的,部分创建的对象。 您可以通过要求将完成回调传递给构造函数并确保调用代码在调用完成回调之后不尝试使用该对象来使其工作,但这不是一种干净的方法。 它还使得异步操作不可能返回一个promise(这是异步设计的未来),因为构造函数必须返回对象引用,因此它不能返回一个promise。

您可以在对象中嵌入promise,但这也很麻烦,因为promise在初始异步操作期间才真正有用。

通常做的是使构造函数只是同步,然后有一个.init()方法来执行异步部分。 这使得代码更清晰,并且与使用promises的实现兼容。

或者,您可以创建一个工厂函数,该函数返回一个解析为对象引用的promise。

第二关,正如您已经知道的那样,您的for循环同步运行。 在进入循环的下一部分之前,它不会“等待”其中的任何异步操作完成。 只要循环的每次调用是独立的并且不依赖于先前的迭代,那就没关系了。 所有你需要知道的是当循环中的所有异步操作完成并使异步操作返回promises并且使用Promise.all()通常是最好的工具。

所以,假设您使用.init()方法方案,其中.init()执行初始化的异步部分,构造函数是同步的,而.init()返回一个promise。 然后,你可以这样做:

// create all the things objects
let things = data.array.map(i => new Thing(i.id)); 

// initialize them all asynchronously
Promise.all(things.map(item => { 
    return item.init();
})).then(function() {
    // all things are asynchronously initialized here
});

或者,使用工厂函数的概念,该函数返回解析为对象的promise:

function newThing(i) {
    let o = new Thing(i.id);
    return o.init().then(function() {
        // resolve to the object itself
        return o;
    });
}

Promise.all(data.array.map(i => newThing(i))).then(things => {
    // all things in the array ready to be used here
});

如果你需要对你的数组迭代进行排序,那么第二次迭代就没有开始,直到第一次迭代的异步部分完成,第三次等待直到第二次迭代完成,等等,那么你就不能使用for循环,因为它只是不这样做。 有几种不同的方法可以进行这种序列化的异步迭代。 您可以在这些其他帖子中看到几种不同的方案:

如何同步一系列承诺?

JavaScript:同步执行一系列承诺

ES6 Promises - 像async.each这样的东西?

如何按顺序执行shell命令?

您可以使用primise.all在for循环之后运行所有promise。然后您可以解析promise.all。

load(file) {
 fs.readFile(file).Then(function (fd){
var data = JSON.parse(fd);
var EachPromise = [ ]; 
for(var i of data.array){
EachPromise.push(new Thing(i.id)); // new Thing is Asynchronous
}
Promise.all(EachPromise)  .then(function (result){
console.log('this is result',result);  
}).Catch(function (error){
console.log('this is error', error);
});
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM