簡體   English   中英

如何打破承諾鏈

[英]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;
}

checkIf返回一個promise,是的checkIf 無法修改

我如何在第一場比賽中脫穎而出? (除了明確拋出錯誤之外的任何其他方式?)

除了明確拋出錯誤之外的任何方式?

你可能需要拋出一些東西,但它不一定是錯誤的。

大多數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.
    });

我添加了破解我自己的promise庫的最近實現的能力。 如果你使用的是ThenFail (你可能沒有),你可以寫下這樣的東西:

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

我會使用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假裝。

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

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

return語句返回一個“then-able”,只是then方法什么都不做。 當從then()函數返回時,then()將嘗試從thenable中獲取結果。 當時的“then”接受回調,但在這種情況下永遠不會被調用。 所以“then()”返回,並且鏈的其余部分的回調不會發生。

您可以創建firstSucceeding函數,該函數將返回第一個成功操作的值或拋出NonSucceedingError

我已經使用過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); }); 

我喜歡到目前為止發布的許多答案,以減輕q自述文件所謂的“厄運金字塔”。 為了便於討論,我將添加我在搜索周圍以查看其他人正在做什么之前插入的模式。 我寫了一個像

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某些庫的承諾可能會返回null以表示除了“打破鏈條”以外的東西,這可能會令人困惑。

這是一個更多的評論/答案的呼吁,而不是“這絕對是做到這一點的方式”答案。

可能會在這里晚會,但我最近發布了一個使用生成器的答案和將回答這個問題的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);

您幾乎可以編寫實際上異步的同步代碼!

希望能幫助到你!

嘗試使用像這樣的庫:

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