Bluebird.each 如果解决了就中断
Bluebird.each break if solved
我想测试数组的每个元素,直到满足条件,然后跳过其余部分。这是我想出的代码,它似乎可以工作,但我不确定它是否真的安全或有意想不到的副作用。欢迎其他解决方案。
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;
}
});
}
});
}
});
如果您试图一次一个地连续枚举玩家,并在发现玩家的好友列表中有空间时中止迭代,您可以更新列表并反馈发生的任何错误,那么这里是一种方法。
工作原理如下:
- 使用 Bluebird 的
Promise.promisifyAll()
自动为 Player
对象创建 returning 方法,这样我们就可以在我们的控制流中使用它们。
- 使用 Bluebird 的
Promise.mapSeries()
一次一个地连续迭代数组。
- 链接
Player.countAsync()
和 Player.updateAsync()
方法,使它们正确排序,return 它们来自 .mapSeries()
,因此在继续迭代到下一个之前等待它们完成数组元素。
- 如果我们找到有房间的玩家并成功更新好友列表,则抛出一个特殊的异常。这将拒绝当前的承诺链并导致
.mapSeries()
停止它的迭代(这就是你所说的你想要的)。
- 在更高级别添加一个
.catch()
来测试特殊拒绝并将其转化为成功解决的承诺。如果是其他错误,则让它作为实际错误继续传播。
代码:
// 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 解析为真值时停止可能更简单:
// 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;
});
}
});
});
我想测试数组的每个元素,直到满足条件,然后跳过其余部分。这是我想出的代码,它似乎可以工作,但我不确定它是否真的安全或有意想不到的副作用。欢迎其他解决方案。
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;
}
});
}
});
}
});
如果您试图一次一个地连续枚举玩家,并在发现玩家的好友列表中有空间时中止迭代,您可以更新列表并反馈发生的任何错误,那么这里是一种方法。
工作原理如下:
- 使用 Bluebird 的
Promise.promisifyAll()
自动为Player
对象创建 returning 方法,这样我们就可以在我们的控制流中使用它们。 - 使用 Bluebird 的
Promise.mapSeries()
一次一个地连续迭代数组。 - 链接
Player.countAsync()
和Player.updateAsync()
方法,使它们正确排序,return 它们来自.mapSeries()
,因此在继续迭代到下一个之前等待它们完成数组元素。 - 如果我们找到有房间的玩家并成功更新好友列表,则抛出一个特殊的异常。这将拒绝当前的承诺链并导致
.mapSeries()
停止它的迭代(这就是你所说的你想要的)。 - 在更高级别添加一个
.catch()
来测试特殊拒绝并将其转化为成功解决的承诺。如果是其他错误,则让它作为实际错误继续传播。
代码:
// 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 解析为真值时停止可能更简单:
// 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;
});
}
});
});