实现混合的承诺.所有的和承诺,解决

Implementing a mix of Promise.all and Promise.settle

本文关键字:承诺 解决 混合 实现      更新时间:2023-09-26

我需要实现一个版本的Promise.all,它会接受一个数组的承诺,并像往常一样返回结果,再加上解决所有的承诺,就像Promise.settleBluebird库中做的那样,除了我不能使用Bluebird,并且必须依赖标准的承诺协议。

实现起来会非常复杂吗?或者在这里问一个如何实现它的想法是不是太过分了?我真的希望没有,所以我请求,如果有人以前实现过它,分享一下如何正确地做它的想法。

这样做的前提是能够在调用完成后需要执行commit/rollback的数据库事务中使用它,并且它不能有松散的承诺仍然试图在事务调用之外解决。

EDIT:提供的另一个问题的链接非常有用,但它不是所问问题的完整答案。通用的settle是一个很好的例子,它帮助很大,但它需要被简化并包装成all逻辑,以适应前面描述的事务场景。

我认为jfriend的解决方案过于复杂,因为它建立在settle之上,它运行一个信号量,并做了很多奇怪的事情,而不是使用内置的原语,如.all

相反,如果我们构建蓝鸟的较新的reflect原语(在本机承诺中实现它),我们可以得到一个更干净的API和实现:

function reflect(promise){
    return promise.then(x => ({state: "fulfilled", value: x}), // arrows, assume nodejs
                        e => ({state: "rejected" , value: e}));
}

在reflect之上,我们可以很容易地构建其他原语:

function settle(promises){
    return Promise.all(promises.map(reflect)); // much cleaner
}

如果我们想等待,然后根据值解析/拒绝,只需:

function allWait(promises){
    return settle(promises).then(results => {
       var firstFailed = results.find(r => r.state === "rejected");
       if(firstFailed) throw firstFailed.value;
       return results; 
    });
}

在另一个问题的通用promiseSettle()函数的基础上,您可以这样做,并拥有通用settle()类型函数和更具体的版本作为它的包装器。这将使您能够执行许多.settle()类型的行为,并拥有自己的特定风味,还可以根据需要构建其他特定风味:

那么,这里是通用的promiseSettle(),它返回所有承诺的状态,并且只有在所有传入的承诺完成时才会解析:

function promiseSettle(promises) {
    return new Promise(function(resolve) {
        var remaining = promises.length;
        // place to store results in original order
        var results = new Array(remaining);
        function checkDone() {
            if (--remaining === 0) {
                resolve(results);
            }
        }
        promises.forEach(function(item, index) {
            // check if the array entry is actually a thenable
            if (typeof item.then === "function") {
                item.then(function(value) {
                    // success
                    results[index] = {state: "fulfilled", value: value};
                    checkDone();
                }, function(err) {
                    // reject error
                    results[index] = {state: "rejected", value: err};
                    checkDone();
                });
            } else {
                // not a thenable, just return the item itself
                results[index] = {state: "fulfilled", value: item}
                --remaining;
            }
        });
        // special case for zero promises passed
        if (remaining === 0) {
            resolve(results);
        }
    });
}

这里有一个包装,它给了你特定的行为:

// Either fulfills with an array of results or
// rejects with the first error, but it does not do either
// until all promises have completed which makes it different than
// promise.all()
function promiseSettleAll(promises) {
    return promiseSettle(promises).then(function(results) {
        for (var i = 0; i < results.length; i++) {
            if (results[i].state !== "fulfilled") {
                // reject with the first error found
                throw results[i].value;
            }
        }
        // all were successful, return just array of values
        return results.map(function(item) {return item.value;});
    });
}

在所有的研究、编写、测试和优化之后,它变成了一个专注于这类事情的库(spex)。

具体来说,方法批处理是实现所描述的逻辑融合的方法。

我不在这里重新发布它的源代码,因为它现在所做的比最初在问题中所寻求的要多得多。