javascript - Bluebird.each 如果解决则中断

标签 javascript for-loop asynchronous mongoose bluebird

我想测试数组的每个元素,直到满足条件,然后跳过其余的。这是我想出的代码,它似乎有效,但我不确定它是否真的安全或有意想不到的副作用。欢迎其他解决方案。

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. 使用 Bluebird 的 Promise.promisifyAll() 自动为 Player 对象创建 Promise 返回方法,以便我们可以在控制流中使用这些方法。
  2. 使用 Bluebird 的 Promise.mapSeries() 一次连续迭代一个数组。
  3. 链接 Player.countAsync()Player.updateAsync() 方法,以便它们正确排序并从 .mapSeries() 返回它们因此它会等待它们完成,然后再继续迭代到下一个数组元素。
  4. 如果我们找到一个有空间的玩家并成功更新其好友列表,则抛出一个特殊的异常。这将拒绝当前的 promise 链并导致 .mapSeries() 停止其迭代(这就是您所说的您想要的)。
  5. 在更高级别添加一个 .catch() 来测试特殊拒绝并将其转换为成功解决的 promise 。如果是其他错误,则让它继续作为实际错误传播。

代码:

// 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;
            });
        }
    });
});

关于javascript - Bluebird.each 如果解决则中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41784895/

相关文章:

node.js - 围绕 async wait 编写一个循环来并行读取和写入文件?

javascript - Nativescript:如何从图库获取照片列表

javascript - 比较运算符没有返回正确的值

JavaScript问题——onMouseOver事件

c++ - for循环的增量语句中的奇数位运算符

apache-flex - 处理异步控制结构(Fluent Interface?)

ios - 从 TimerCallback 实例调用时,调用 Dropbox API Client.Files.DownloadAsync 不会返回元数据

Javascript - 当页面中没有元素具有焦点时,将元素聚焦在 keydown 上

bash - 如何在bash中使用变量名生成for循环序号?

css - Sass:使用@for 循环更改颜色