如果你想連續枚舉的球員之一的時間和中止迭代,當你找到的房間在他們的好友列表一個播放器,您可以更新列表,並傳送回所發生的任何錯誤,那麼這就是做這件事的一種方法。
這裏是如何工作的:
- 使用藍鳥的
Promise.promisifyAll()
自動做出承諾返回方法的Player
對象,這樣我們就可以使用那些在我們的控制流。使用Bluebird的Promise.mapSeries()
來連續迭代數組。
- 連鎖
Player.countAsync()
和Player.updateAsync()
方法,以便它們順序正確並從.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);
});
它可以更簡單,使自己的音序器,當第一承諾解析爲truthy值停止:
// 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;
});
}
});
});
假設'球員。 count()'和'Player.update()'是異步操作,這是無法正常工作的。對'.each()'的回調需要返回一個表示回調中的異步操作的promise。你擁有它的方式,回調代碼之外沒有辦法知道什麼時候完成。另外,既然你有藍鳥,你可能想使用'Promise.any()'來捕獲解決你想要的方式的第一個值。 – jfriend00
爲了給你提供一個更好的方法,我們需要知道你正在嘗試完成的內容,無論你是想以嚴格的順序處理數組,還是多個玩家可以並行處理,你試圖達到什麼樣的確切結果。 – jfriend00
@ jfriend00我需要查看一系列可用的機器人,並檢查其中哪些機器人在其朋友列表中有剩餘空間。我使用Model.count來獲取列表中的當前朋友數量。理想情況下,應該按照它們在數組中的順序檢查它們。當找到一個空插槽的機器人時,我想將機器人名稱保存到數據庫,如果該操作成功,則應該解決該承諾,並跳過所有其他迭代/元素。 – Trax