打破Bluebird .each()过程

时间:2016-04-29 11:44:46

标签: bluebird

我正在从Async转换为Bluebird,无法弄清楚如何打破循环

这就是我想要实现的目标:

  1. 循环遍历数据数组。
  2. 对于每个项目,检查它是否存在于数据库中。
  3. 一个项添加到数据库(第一个不存在的项),然后退出.each()循环。
  4. 任何帮助都将受到高度赞赏。

1 个答案:

答案 0 :(得分:5)

Bluebird没有针对该类型操作的内置函数,并且它很难适应promise迭代模型,因为迭代器会返回一个并不真正给出的单个值(一个promise)您有机会回复成功/错误并停止迭代。

使用拒绝停止迭代

您可以使用Promise.each(),但是您必须使用编码拒绝才能停止迭代:

var data = [...];

Promise.each(data, function(item, index, length) {
    return checkIfItemExists(item).then(function(exists) {
        if (!exists) {
            return addItemToDb(item).then(function() {
                // successfully added item to DB
                // lets reject now to stop the iteration
                // but reject with a custom signature that can be discerned from an actual error
                throw {code: "success", index: index};
            });
        }
    })
}).then(function() {
    // finished the iteration, but nothing was added to the DB
}, function(err) {
    if (typeof err === "object" && err.code === "success") {
        // success
    } else {
        // some sort of error here
    }
});

如果必须定期使用,可以将此结构放入可重用的函数/方法中。你只需要为被拒绝的承诺采用一种约定,这种约定实际上只是为了成功地停止迭代,而不是实际的错误。

这似乎是一个有趣的,并非所有那种不常见的需求,但我还没有看到任何特定的定义结构与Promises处理这类问题。

如果在上面的场景中感觉像重载一个拒绝是太多的黑客攻击(它有点做),那么你可以编写自己的迭代方法,使用解析的值约定告诉迭代器什么时候停止:

自定义迭代

Promise.eachStop = function(array, fn) {
    var index = 0;

    return new Promise(function(resolve, reject) {

        function next() {
            if (index < array.length) {
                // chain next promise
                fn(array[index], index, array.length).then(function(result) {
                    if (typeof result === "object" && result.stopIteration === true) {
                        // stopped after processing index item
                        resolve(index);
                    } else {
                        // do next iteration
                        ++index;
                        next();
                    }
                }, reject);
            } else {
                // finished iteration without stopping
                resolve(null);
            }
        }

        // start the iteration
        next();
    });
}

如果迭代器使用一个对象具有属性stopIteration: true的值解析,那么迭代器将停止。

最终的承诺将拒绝任何地方是否存在错误,并且如果迭代器完成并且从未停止或者使用数字作为迭代停止的索引,则将以null的值解析。

您可以这样使用:

Promise.eachStop(data, function(item, index, length) {
    return checkIfItemExists(item).then(function(exists) {
        if (!exists) {
            return addItemToDb(item).then(function() {
                // return special coded object that has stopIteration: true
                // to tell the iteration engine to stop
                return {stopIteration: true};
            });
        }
    })
}).then(function(result) {
    if (result === null) {
        // finished the iteration, but nothing was added to the DB
    } else {
        // added result item to the database and then stopped further processing
    }
}, function(err) {
    // error
});

告诉迭代器是否跳过其工作的标志变量

在考虑这个问题时,我提出了另一种方法来实现这一点,允许Promise.each()迭代运行完成,但是设置一个更高的范围变量,它告诉你的迭代器何时应该跳过它的工作:

var data = [...];
// set property to indicate whether we're done or not
data.done = false;

Promise.each(data, function(item, index, length) {
    if (!data.done) {
        return checkIfItemExists(item).then(function(exists) {
            if (!exists) {
                return addItemToDb(item).then(function() {
                    data.done = true;
                });
            }
        })
    }
}).then(function() {
    // finished
}, function(err) {
    // error
});