使用promises实现回退

时间:2015-06-08 08:55:33

标签: javascript jquery promise jquery-deferred

这是一种常见的模式,我们在数据源列表中级联,第一次成功打破了这样的链:

input_items

等等。如果 getDataN()函数是异步的,那么它会导致我们'回调地狱':

var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();

其中的实现可能类似于:

var data;
getData1(function() {
    getData2(function () {
        getData3(function () { alert('not found'); })
    })
});

...有了承诺,我希望写下这样的东西:

function getData1(callback) {
    $.ajax({
        url: '/my/url/1/',
        success: function(ret) { data = ret },
        error: callback
    });
 }

其中第二个$.when(getData1()) .then(function (x) { data = x; }) .fail(function () { return getData2(); }) .then(function (x) { data = x; }) .fail(function () { return getData3(); }) .then(function (x) { data = x; }); 实际上是指第一个.then的返回值,它本身就是一个承诺,我理解它被链接作为后续链步骤的输入。

显然我错了,但写这个的正确方法是什么?

2 个答案:

答案 0 :(得分:11)

在大多数承诺图书馆中,您可以像@ mido22的答案一样链接.fail().catch(),但jQuery的.fail()不会#&#t} 34;处理"这样的错误。保证始终传递输入承诺(具有未改变状态),这将不允许所需的" break"如果/何时成功发生的级联。

唯一可以返回具有不同状态(或不同值/原因)的promise的jQuery Promise方法是.then()

因此,您可以通过在每个阶段将下一步指定为当时的错误处理程序来编写一个继续出错的链。

function getDataUntilAsyncSuccess() {
    return $.Deferred().reject()
        .then(null, getData1)
        .then(null, getData2)
        .then(null, getData3);
}
//The nulls ensure that success at any stage will pass straight through to the first non-null success handler.

getDataUntilAsyncSuccess().then(function (x) {
    //"success" data is available here as `x`
}, function (err) {
    console.log('not found');
});

但实际上,您可能更常创建一个函数数组或数据对象,这些函数或数据对象在Array方法.reduce()的帮助下依次调用。

例如:

var fns = [
    getData1,
    getData2,
    getData3,
    getData4,
    getData5
];      

function getDataUntilAsyncSuccess(data) {
    return data.reduce(function(promise, fn) {
        return promise.then(null, fn);
    }, $.Deferred().reject());// a rejected promise to get the chain started
}

getDataUntilAsyncSuccess(fns).then(function (x) {
    //"success" data is available here as `x`
}, function (err) {
    console.log('not found');
});

或者,这可能是一个更好的解决方案:

var urls = [
    '/path/1/',
    '/path/2/',
    '/path/3/',
    '/path/4/',
    '/path/5/'
];      

function getDataUntilAsyncSuccess(data) {
    return data.reduce(function(promise, url) {
        return promise.then(null, function() {
            return getData(url);// call a generalised `getData()` function that accepts a URL.
        });
    }, $.Deferred().reject());// a rejected promise to get the chain started
}

getDataUntilAsyncSuccess(urls).then(function (x) {
    //"success" data is available here as `x`
}, function (err) {
    console.log('not found');
});

答案 1 :(得分:1)

作为一个初学者,遇到了同样的问题,我才意识到gcloud iam service-accounts get-iam-policy ingester@xxx-123.iam.gserviceaccount.com async变得更简单了:

同步模式

await

现在可以轻松地应用于异步代码:

var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();

仅记得在使用此代码的函数中添加一个let data = await getData1(); if (!data) data = await getData2(); if (!data) data = await getData3();