【问题标题】:nodejs looping through array with async calls?nodejs通过异步调用循环遍历数组?
【发布时间】:2017-05-10 15:03:34
【问题描述】:

我正在尝试遍历一个数组,该数组将new 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 类转换为同步类,删除对类内函数的异步调用,并首先实例化循环中的所有事物,然后调用 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);
  });
}

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    您必须在 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)
      });
    }
    

    【讨论】:

      【解决方案2】:

      你可以使用 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);
      });
      }
      

      【讨论】:

        【解决方案3】:

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

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

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

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

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

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

        // 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
        });
        

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

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

        如果您需要对数组迭代进行排序,以便第二次迭代直到第一次迭代的异步部分完成后才开始,第三次等到第二次迭代完成,依此类推,那么您不能使用 @987654333 @ 循环,因为它根本不能那样工作。有几种不同的方法可以进行这样的序列化异步迭代。您可以在这些其他帖子中看到几种不同的方案:

        How to synchronize a sequence of promises?

        JavaScript: Perform a chain of promises synchronously

        ES6 Promises - something like async.each?

        How can I execute shell commands in sequence?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-22
          相关资源
          最近更新 更多