如何打破承诺链

how to break promise chain

我以这样的方式承诺,

function getMode(){
    var deferred = Promise.defer();

    checkIf('A')
    .then(function(bool){
        if(bool){
            deferred.resolve('A');
        }else{
            return checkIf('B');
        }
    }).then(function(bool){
        if(bool){
            deferred.resolve('B');
        }else{
            return checkIf('C');
        }
    }).then(function(bool){
        if(bool){
            deferred.resolve('C');
        }else{
            deferred.reject();
        }
    });

    return deferred.promise;
}

checkIfreturns一个承诺,是checkIf不可修改.

如何在第一场比赛中摆脱束缚? (除了显式抛出错误之外还有什么方法吗?)

您可以创建一个 firstSucceeding 函数,它将 return 第一个成功操作的值或抛出 NonSucceedingError.

我使用过 ES6 promises,但您可以调整算法以支持您选择的 promise 接口。

function checkIf(val) {
    console.log('checkIf called with', val);
    return new Promise(function (resolve, reject) {
        setTimeout(resolve.bind(null, [val, val === 'B']), 0);
    });
}

var firstSucceeding = (function () {
    
    return function (alternatives, succeeded) {
        var failedPromise = Promise.reject(NoneSucceededError());  
        return (alternatives || []).reduce(function (promise, alternative) {
            return promise.then(function (result) {
                    if (succeeded(result)) return result;
                    else return alternative();
                }, alternative);
        }, failedPromise).then(function (result) {
            if (!succeeded(result)) throw NoneSucceededError();
            return result;
        });
     }
    
    function NoneSucceededError() {
        var error = new Error('None succeeded');
        error.name = 'NoneSucceededError';
        return error;
    }
})();

function getMode() {
    return firstSucceeding([
        checkIf.bind(null, 'A'),
        checkIf.bind(null, 'B'),
        checkIf.bind(null, 'C')
    ], function (result) {
        return result[1] === true;
    });
}

getMode().then(function (result) {
    console.log('res', result);
}, function (err) { console.log('err', err); });

我只想使用 coroutines/spawns,这导致 更简单的代码:

function* getMode(){
    if(yield checkIf('A'))
        return 'A';
    if(yield checkIf('B'))
        return 'B';
    if(yield checkIf('C'))
        return 'C';
    throw undefined; // don't actually throw or reject with non `Error`s in production
}

如果您没有发电机,那么总是有 traceur 或 6to5。

我想你不想要这里的连锁店。以同步方式,你会写

function getMode(){
    if (checkIf('A')) {
        return 'A';
    } else {
        if (checkIf('B')) {
            return 'B';
        } else {
            if (checkIf('C')) {
                return 'C';
            } else {
                throw new Error();
            }
        }
    }
}

这就是它应该如何转化为承诺:

function getMode(){
    checkIf('A').then(function(bool) {
        if (bool)
            return 'A';
        return checkIf('B').then(function(bool) {
            if (bool)
                return 'B';
            return checkIf('C').then(function(bool) {
                if (bool)
                    return 'C';
                throw new Error();
            });
        });
    });
}

承诺中没有if else-扁平化。

Any way other than explicitly throwing error?

您可能需要抛出一些东西,但不一定是错误。

大多数 promise 实现都有方法 catch 接受第一个参数作为错误类型(但不是全部,也不是 ES6 promise),这在这种情况下会很有帮助:

function BreakSignal() { }

getPromise()
    .then(function () {
        throw new BreakSignal();
    })
    .then(function () {
        // Something to skip.
    })
    .catch(BreakSignal, function () { })
    .then(function () {
        // Continue with other works.
    });

我在我自己的 promise 库的最近实现中添加了中断功能。如果你使用 ThenFail(你可能不会),你可以这样写:

getPromise()
    .then(function () {
        Promise.break;
    })
    .then(function () {
        // Something to skip.
    })
    .enclose()
    .then(function () {
        // Continue with other works.
    });

我喜欢到目前为止发布的很多答案,这些答案减轻了 q readme 所谓的 "pyramid of doom"。为了便于讨论,我将添加我在四处搜索以查看其他人在做什么之前找出的模式。我写了一个像

这样的函数
var null_wrap = function (fn) {
  return function () {
    var i;
    for (i = 0; i < arguments.length; i += 1) {
      if (arguments[i] === null) {
        return null;
      }
    }
    return fn.apply(null, arguments);
  };
};

我做了一些与@vilicvane 的回答完全类似的事情,除了 throw new BreakSignal(),我写了 return null,并将所有后续 .then 回调包装在 null_wrap 喜欢

then(null_wrap(function (res) { /* do things */ }))

我认为这是一个很好的答案 b/c 它避免了很多缩进,并且 b/c OP 特别要求一个没有 throw 的解决方案。也就是说,我可能会回去使用更像@vilicvane 所做的事情 b/c 一些图书馆的承诺可能 return null 来指示 "break the chain" 以外的东西,那可能是令人困惑。

与 "this is definitely the way to do it" 答案相比,这更像是对 comments/answers 的呼吁。

可能来晚了,但我最近发布了一个使用生成器和 co 库来回答这个问题的答案(参见解决方案 2):

代码如下:

const requestHandler = function*() {

        const survey = yield Survey.findOne({
            _id: "bananasId"
        });

        if (survey !== null) {
            console.log("use HTTP PUT instead!");
            return;
        }

        try {
            //saving empty object for demonstration purposes
            yield(new Survey({}).save());
            console.log("Saved Successfully !");
            return;
        }
        catch (error) {
            console.log(`Failed to save with error:  ${error}`);
            return;
        }

    };

    co(requestHandler)
        .then(() => {
            console.log("finished!");
        })
        .catch(console.log);

您几乎可以编写实际上是异步的同步代码!

希望对您有所帮助!

你可以使用 return { then: function() {} };

.then(function(bool){
    if(bool){
        deferred.resolve('A');
        return { then: function() {} }; // end/break the chain
    }else{
        return checkIf('B');
    }
})

return 语句 return 是一个 "then-able",只是 then 方法什么都不做。 当从 then() 中的函数 returned 时, then() 将尝试从 thenable 中获取结果。 then-able 的 "then" 接受回调,但在这种情况下永远不会调用。所以 "then()" returns,以及链的其余部分的回调不会发生。

尝试使用像这样的库:

https://www.npmjs.com/package/promise-chain-break

    db.getData()
.then(pb((data) => {
    if (!data.someCheck()) {
        tellSomeone();

        // All other '.then' calls will be skiped
        return pb.BREAK;
    }
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
    console.error(error);
});