2017-01-21 26 views
0

我想測試數組中的每個元素,直到滿足條件,然後跳過其餘部分。這是我提出的代碼,它似乎正在工作,但我不確定它的實際安全或它有意想不到的副作用。歡迎其他解決方案。Bluebird.each break如果解決了

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

假設'球員。 count()'和'Player.update()'是異步操作,這是無法正常工作的。對'.each()'的回調需要返回一個表示回調中的異步操作的promise。你擁有它的方式,回調代碼之外沒有辦法知道什麼時候完成。另外,既然你有藍鳥,你可能想使用'Promise.any()'來捕獲解決你想要的方式的第一個值。 – jfriend00

+0

爲了給你提供一個更好的方法,我們需要知道你正在嘗試完成的內容,無論你是想以嚴格的順序處理數組,還是多個玩家可以並行處理,你試圖達到什麼樣的確切結果。 – jfriend00

+0

@ jfriend00我需要查看一系列可用的機器人,並檢查其中哪些機器人在其朋友列表中有剩餘空間。我使用Model.count來獲取列表中的當前朋友數量。理想情況下,應該按照它們在數組中的順序檢查它們。當找到一個空插槽的機器人時,我想將機器人名稱保存到數據庫,如果該操作成功,則應該解決該承諾,並跳過所有其他迭代/元素。 – Trax

回答

1

如果你想連續枚舉的球員之一的時間和中止迭代,當你找到的房間在他們的好友列表一個播放器,您可以更新列表,並傳送回所發生的任何錯誤,那麼這就是做這件事的一種方法。

這裏是如何工作的:

  1. 使用藍鳥的Promise.promisifyAll()自動做出承諾返回方法的Player對象,這樣我們就可以使用那些在我們的控制流。使用Bluebird的Promise.mapSeries()來連續迭代數組。
  2. 連鎖Player.countAsync()Player.updateAsync()方法,以便它們順序正確並從.mapSeries()返回它們,以便在繼續迭代到下一個數組元素之前等待它們完成。
  3. 如果我們找到有空位的玩家併成功更新好友列表,請拋出一個特殊的異常。這將拒絕當前的承諾鏈並導致.mapSeries()停止它的迭代(這就是你所說的你想要的)。
  4. 在更高層次添加.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; 
      }); 
     } 
    }); 
}); 
+0

與您的第二個建議,謝謝你, – Trax