我有一个函数,它执行一系列异步操作,然后执行其他异步操作的循环。我想知道什么时候一切都完成了。这似乎是一个让我沉浸在承诺中的好时机。
我的代码在promise之前的状态可以归结为这样的东西(希望在简化过程中我没有让这个例子变得无用):
myClass.prototype.doMaintenance = function() {
var types = ['choreType1', 'choreType2'];
types.forEach(function(choreType) {
// find all chores of the type with score 0 (need to be done)
redisClient.zrangebyscore('chores:'+choreType, 0, 0, function(err, chores) {
if (!err) {
chores.foreach(function(chore) {
doChore(chore, function(err, result){
// chore complete!
});
})
}
});
});
}
我遍历一个循环,对循环中的每个项进行异步数据库调用,并遍历返回的结果,对每个结果进行另一个异步调用。使用回调来传递所有家务都已完成的通知,这充其量看起来是丑陋的。因此,我的目标是:建立一个承诺,当所有的家务都完成时,它就会解决。
我面临两个困难。一个是简单地使promise语法正确。我将在下面向您展示我尝试过的内容。首先,一个可能导致此问题无法解决的问题是:假设第一个数据库查询返回时只有一件家务事。我(不知怎么的)把它作为整体"完成所有家务"承诺的一部分。现在我回过头来看看下一类家务活的清单。如果在此期间第一件家务事完成了呢?在添加其他家务之前,所做的所有家务承诺都会得到满足,并会得到解决。
我在node.js
环境中使用Q
库。我使用Redis
,但它可以是任何异步数据源。
myClass.prototype.doMaintenance = function() {
var types = ['choreType1', 'choreType2'];
var typePromises = [];
types.forEach(function(choreType) {
// find all chores of the type with score 0 (need to be done)
Q.npost(redisClient, 'zrangebyscore', ['chores:'+choreType, 0, 0]).done(chores) {
var chorePromises = [];
chores.foreach(function(chore) {
chorePromises.push(doChore(chore)); // doChore returns a promise
});
typePromises.push(Q.all(chorePromises));
});
});
return Q.all(typePromises); // at this point, typePromises is empty. Bummer!
}
我一直在尝试构建(还没有完全实现)一个promise,它是typePromises的集合,反过来又是chorePromises集合。
我想我需要的是一个结构,上面写着"我保证一有空就帮你完成所有家务。"这开始让我大吃一惊。任何指导(包括完全使用不同的模式)都将不胜感激。
您正在异步构建typePromises
的列表,当您调用Q.all(typePromises)
时,它仍然是空的。相反,您需要立即返回数据库结果的promise,您可以立即将其收集到列表中。如果您还不知道这些承诺的返回值是多少——不用担心,可以使用then
来编写任务,比如在redis结果到达后获得Q.all(chorePromises)
。
我还建议使用map
,而不是在each
循环中推送一个数组——这也有助于确保立即构建promise。
myClass.prototype.doMaintenance = function() {
var types = ['choreType1', 'choreType2'];
var typePromises = types.map(function(choreType) {
// find all chores of the type with score 0 (need to be done)
return Q.npost(redisClient, 'zrangebyscore', ['chores:'+choreType, 0, 0]).then(function(chores) {
var chorePromises = chores.map(doChore); // doChore returns a promise
return Q.all(chorePromises);
}); // then returns a promise
});
return Q.all(typePromises);
}