中断Bluebird .each()进程

Ron*_*eva 2 bluebird

我正在从异步转换为蓝鸟,无法弄清楚如何打破循环

这是我要实现的目标:

  1. 遍历数据数组。
  2. 对于每个项目,检查它是否存在于数据库中。
  3. 向数据库添加一项(不存在的第一项),然后退出.each()循环。

任何帮助将不胜感激。

jfr*_*d00 6

蓝鸟没有针对这种类型的操作的内置函数,并且由于迭代器返回单个值(一个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
    }
});
Run Code Online (Sandbox Code Playgroud)

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

这看起来确实是一个有趣的问题,并且不是所有不常见的需求,但是我还没有看到使用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();
    });
}
Run Code Online (Sandbox Code Playgroud)

在这里,如果迭代器解析为具有对象属性的值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
});
Run Code Online (Sandbox Code Playgroud)

标记变量,告知迭代器是否跳过其工作

在进一步考虑这一点时,我想出了另一种方式来实现此目的,即允许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
});
Run Code Online (Sandbox Code Playgroud)