简体   繁体   English

如何打破承诺链

[英]how to break promise chain

I a promise in such fashion, 我是这样的承诺,

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

checkIf returns a promise, and yes checkIf cannot be modified . checkIf返回一个promise,是的checkIf 无法修改

How do I break out of the chain at the first match? 我如何在第一场比赛中脱颖而出? (any way other than explicitly throwing error?) (除了明确抛出错误之外的任何其他方式?)

Any way other than explicitly throwing error? 除了明确抛出错误之外的任何方式?

You may need to throw something, but it does not have to be an error. 你可能需要抛出一些东西,但它不一定是错误的。

Most promise implementations have method catch accepting the first argument as error type (but not all, and not ES6 promise), it would be helpful under this situation: 大多数promise实现都有方法catch接受第一个参数作为错误类型(但不是全部,而不是ES6的承诺),在这种情况下它会有所帮助:

function BreakSignal() { }

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

I add the ability to break in the recent implementation of my own promise library. 我添加了破解我自己的promise库的最近实现的能力。 And if you were using ThenFail (as you would probably not), you can write something like this: 如果你使用的是ThenFail (你可能没有),你可以写下这样的东西:

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

I would just use coroutines/spawns , this leads to much simpler code: 我会使用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
}

If you don't have generators then there's always traceur or 6to5. 如果你没有发电机那么总是有traceur或6to5。

I think you don't want a chain here. 我想你不想要链条。 In a synchronous fashion, you'd have written 以同步的方式,你已经写过了

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

and this is how it should be translated to promises: 这就是它应该如何转化为承诺:

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

There is no if else -flattening in promises. 承诺中没有if else假装。

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

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

The return statement returns a "then-able", only that the then method does nothing. return语句返回一个“then-able”,只是then方法什么都不做。 When returned from a function in then(), the then() will try to get the result from the thenable. 当从then()函数返回时,then()将尝试从thenable中获取结果。 The then-able's "then" takes a callback but that will never be called in this case. 当时的“then”接受回调,但在这种情况下永远不会被调用。 So the "then()" returns, and the callback for the rest of the chain does not happen. 所以“then()”返回,并且链的其余部分的回调不会发生。

You could create a firstSucceeding function that would either return the value of the first succeeded operation or throw a NonSucceedingError . 您可以创建firstSucceeding函数,该函数将返回第一个成功操作的值或抛出NonSucceedingError

I've used ES6 promises, but you can adapt the algorithm to support the promise interface of your choice. 我已经使用过ES6承诺,但您可以调整算法以支持您选择的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); }); 

i like a lot of the answers posted so far that mitigate what the q readme calls the "pyramid of doom". 我喜欢到目前为止发布的许多答案,以减轻q自述文件所谓的“厄运金字塔”。 for the sake of discussion, i'll add the pattern that i plunked out before searching around to see what other people are doing. 为了便于讨论,我将添加我在搜索周围以查看其他人正在做什么之前插入的模式。 i wrote a function like 我写了一个像

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

and i did something totally analogous to @vilicvane's answer, except rather than throw new BreakSignal() , i'd written return null , and wrapped all subsequent .then callbacks in null_wrap like 和我做了完全类似于@ vilicvane的答案,除了不throw new BreakSignal()我写return null ,并包裹所有后续.then回调在null_wrap

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

i think this is a good answer b/c it avoids lots of indentation and b/c the OP specifically asked for a solution that doesn't throw . 我认为这是一个很好的答案b / c它避免了大量的缩进和b / c OP专门要求一个不throw的解决方案。 that said, i may go back and use something more like what @vilicvane did b/c some library's promises might return null to indicate something other than "break the chain", and that could be confusing. 那说,我可以回去使用更像@vilicvane做的事情b / c某些库的承诺可能会返回null以表示除了“打破链条”以外的东西,这可能会令人困惑。

this is more a call for more comments/answers than a "this is definitely the way to do it" answer. 这是一个更多的评论/答案的呼吁,而不是“这绝对是做到这一点的方式”答案。

Probably coming late the party here, but I recently posted an answer using generators and the co library that would answer this question (see solution 2): 可能会在这里晚会,但我最近发布了一个使用生成器的答案和将回答这个问题的co库(参见解决方案2):

The code would be something like: 代码如下:

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

You would pretty much write synchronous code that would be in reality asynchronous ! 您几乎可以编写实际上异步的同步代码!

Hope it helps! 希望能帮助到你!

Try to use libs like thisone: 尝试使用像这样的库:

https://www.npmjs.com/package/promise-chain-break 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);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM