2016-12-26 28 views
3

我想遍历一个数组,它将new Thing推到列表中,在Thing内部它执行一些自己的异步调用。我如何以同步方式迭代数组,因为callback要求列表中的数据正常工作。由于我的for循环是同步的并且执行了一些异步调用,所以如果完成了调用,则在列表之前调用该回调。nodejs通过异步调用数组循环?

我不知道我将如何通过数组进行迭代,并完成所有的工作做回调

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

回答

2

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

您可以使用primise.all后的for循环运行所有的承诺那么你可以解决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); 
}); 
} 
2

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

您可以将承诺嵌入对象中,但这也很麻烦,因为承诺在最初的异步操作中才非常有用。

反而经常做的事情是让构造函数只能是同步的,然后有一个做异步部分的方法.init()。这使得代码更简洁,并且与使用promise的实现兼容。

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

其次,如您所知,您的for循环同步运行。在进入循环的下一部分之前,它不会“等待”它内部的任何异步操作完成。只要循环的每次调用都是分开的,并且不依赖于先前的迭代,那就没有问题了。所有你需要知道的是当循环中的所有异步操作完成并且使你的异步操作返回承诺并且使用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 
}); 

如果需要排序的数组迭代,所以第二次迭代没有开始,直到第一次迭代的异步部分完成,第三次等待,直到第二次迭代完成,等等,那么你不能使用for循环,因为它不会以这种方式工作。有几种不同的方法来做这种序列化的异步迭代。你可以看到在这些其他职位几种不同的方案:

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?