Bluebird.each中断,如果解决了 [英] Bluebird.each break if solved
问题描述
我想测试数组的每个元素,直到满足条件,然后跳过其余的部分.这是我想出的代码,它似乎可以正常工作,但是我不确定它是否真正安全或有意想不到的副作用.欢迎其他解决方案.
I want to test each element of an array until a condition is met then skip the rest. This is the code I came up with, it seems to be working but I'm not sure if its actually safe or it has unexpected side effects. Other solutions are welcomed.
let buddyAdded = false;
replicaArr = _.keys(ReplicaList);
Promise.each(replicaArr, function(replicaname) {
if (!buddyAdded) {
Player.count({
'buddyList': replicaname
}, function(err, result) {
if (err) {
} else if (result < 990) {
Player.update({
'_id': buddyObj.buddyId
}, {
'buddyList': replicaname
}, function(err) {
if (err) {
} else {
ReplicaList[replicaname].send(buddyObj);
buddyAdded = true;
// success stop here and skip all the other array elements
return;
}
});
}
});
}
});
推荐答案
如果您要一次枚举一个玩家,并在好友列表中找到有空间的玩家时中止迭代,则可以更新列出并传达发生的任何错误,那么这是一种解决方法.
If you're trying to enumerate the players serially one at a time and abort the iteration when you find a player with room in their buddy list that you can update the list and communicate back any errors that happen, then here's one way of doing it.
这是它的工作方式:
- 使用Bluebird的
Promise.promisifyAll()
为Player
对象自动做出承诺返回方法,以便我们可以在控制流中使用这些方法. - 使用Bluebird的
Promise.mapSeries()
一次一次迭代数组. - 绑定
Player.countAsync()
和Player.updateAsync()
方法,以便它们正确排序并从.mapSeries()
返回它们,以便在继续迭代到下一个数组元素之前等待它们完成. - 如果我们找到一个有空间的玩家并成功更新了其好友列表,则抛出一个特殊的异常.这将拒绝当前的Promise链,并导致
.mapSeries()
停止其迭代(这就是您想要的内容). - 在更高级别添加
.catch()
来测试特殊拒绝,并将其转变为成功的已解决承诺.如果是其他错误,则让该错误继续作为实际错误传播.
- Use Bluebird's
Promise.promisifyAll()
to automatically make promise returning methods for thePlayer
object so we can then use those in our control flow. - Use Bluebird's
Promise.mapSeries()
to iterate the array serially one at a time. - Chain the
Player.countAsync()
andPlayer.updateAsync()
methods so they sequence properly and return them from.mapSeries()
so it waits for them to complete before continuing the iteration to the next array element. - If we find a player with room and successfully update its buddy list, then throw a specially exception. This will reject the current promise chain and cause
.mapSeries()
to stop it's iteration (which is what you said you wanted). - Add a
.catch()
at the higher level that tests for the special rejection and turns it into a successful resolved promise. If it's some other error, then let that continue to propagate as an actual error.
代码:
// Promisify the Player object so the methods
// this would usually be done wherever this module is loaded
Player = Promise.promisifyAll(Player);
// create a special subclass of Error for our short circuit
PlayerUpdateDone extends Error {
constructor(name) {
super();
this.name = name;
}
}
// put logic into a function to make it cleaner to use
function addToBuddyList(replicaArr) {
return Promise.mapSeries(replicaArr, function(replicaname) {
return Player.countAsync({buddyList: replicaname}).then(function(result) {
// if room left in buddy list
if (result < 990) {
return Player.updateAsync({'_id': buddyObj.buddyId}, {'buddyList': replicaname}).then(function() {
ReplicaList[replicaname].send(buddyObj);
// throw special exception to abort .mapSeries()
// and stop further processing
throw new PlayerUpdateDone(replicaname);
});
}
});
}).then(function() {
// if it gets here, there were no players with rooms so just return null
return null;
}).catch(function(result) {
// we used a special rejection as a shortcut to stop the mapSeries from executing
// the rest of the series
if (result instanceof PlayerUpdateDone) {
// change this special rejection into a result
return result.name;
}
// must have been regular error so let that propagate
throw result;
});
}
// sample usage
addToBuddyList(replicaArr).then(function(name) {
if (name) {
console.log(`Updated player ${name}`);
} else {
console.log("No players with room in their buddy list");
}
}).catch(function(err) {
// error here
console.log(err);
});
使自己的音序器在第一个promise解析为真实值时停止运行可能更简单:
It may be simpler to make your own sequencer that stops when the first promise resolves to a truthy value:
// fn will be passed each array element in sequence
// fn should return a promise that when resolved with a truthy value will stop the iteration
// and that value will be the resolved value of the promise that is returned from this function
// Any rejection will stop the iteration with a rejection
Promise.firstToPassInSequence = function(arr, fn) {
let index = 0;
function next() {
if (index < arr.length) {
return Promise.resolve().then(function() {
// if fn() throws, this will turn into a rejection
// if fn does not return a promise, it is wrapped into a promise
return Promise.resolve(fn(arr[index++])).then(function(val) {
return val ? val : next();
});
});
}
// make sure we always return a promise, even if array is empty
return Promise.resolve(null);
}
return next();
};
Promise.firstToPassInSequence(replicaArr, function(replicaname) {
return Player.countAsync({buddyList: replicaname}).then(function(result) {
// if room left in buddy list
if (result < 990) {
return Player.updateAsync({'_id': buddyObj.buddyId}, {'buddyList': replicaname}).then(function() {
ReplicaList[replicaname].send(buddyObj);
return replicaname;
});
}
});
});
这篇关于Bluebird.each中断,如果解决了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!