Javascript 使用承诺实现回退
这是一种常见的模式,我们在数据源列表中级联,第一次成功地打破了这样的链条:Javascript 使用承诺实现回退,javascript,jquery,promise,jquery-deferred,Javascript,Jquery,Promise,Jquery Deferred,这是一种常见的模式,我们在数据源列表中级联,第一次成功地打破了这样的链条: var data = getData1(); if (!data) data = getData2(); if (!data) data = getData3(); $.when(getData1()) .then(function (x) { data = x; }) .fail(function () { return getData2(); }) .then(function (x) { dat
var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();
$.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; });
等等。但是,如果getDataN()函数是异步的,则会导致“回调地狱”:
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
});
}
…带着承诺,我希望能写下这样的东西:
var data = getData1();
if (!data) data = getData2();
if (!data) data = getData3();
$.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
实际上是指第一个.fail
的返回值,它本身就是一个承诺,我理解它是作为后续链步骤的输入链接在一起的
显然我错了,但是写这篇文章的正确方法是什么呢?在大多数promise库中,您可以像@mido22的答案那样链接
.fail()
或.catch()
,但是jQuery的.fail()
并不能“处理”这样的错误。它总是保证传递输入承诺(状态不变),如果成功发生,这将不允许所需的级联“中断”
唯一可以返回具有不同状态(或不同值/原因)的承诺的jQuery Promise方法是.then()
因此,您可以通过在每个阶段将下一步指定为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');
});
但在实践中,您可能更典型地创建一个函数或数据对象数组,这些函数或数据对象在数组方法.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');
});
作为一个初学者,我遇到了同样的问题,我刚刚意识到使用
async
和await
,这个问题变得简单多了:
同步模式
现在可以轻松地应用于异步代码:
let data = await getData1();
if (!data) data = await getData2();
if (!data) data = await getData3();
请记住向使用此代码的函数中添加一个
async
。getData1
应返回$。ajax
承诺。首先,您可以继续传递承诺(作为第二个参数),一旦成功,您就完成了承诺,失败时运行回调。我认为在异步函数上不能这样做。为什么不使用递归回调和简单if-else?什么是$。with()
?你的意思是$。when()
?$。当你只有一个承诺时,when()
是不需要的。如果您修复了getData1()
以返回一个承诺,那么您只需执行getData1()
。然后(…)`。这太棒了@Roamer-1888。这很有趣,我还没有看到任何这种解决方案的例子,但这是非常干净的。我会接受它作为一个解决方案,当我验证它的工作+1我最近多次回答了类似的问题,但问题的措辞都非常不同,这可能就是为什么很难找到这些问题的原因。你几乎需要知道答案才能知道要搜索什么,这就是为什么我不断提供新答案,而不是以前的链接。顺便说一句,@Bergi首先让我正确地使用了这种方法。很抱歉,现在没有时间去挖掘链接。@Roamer-1888:Heh,很高兴听到:-)但是我已经在一千个地方使用了reduce
方法,现在还没有一个我们可以链接的规范。@Bergi,完全同意-我们需要一个规范,它需要至少涵盖两个变体-第一次成功时中断和第一次失败时中断。我想应该涵盖其他变体,先解决后继续。还有其他吗?请记住,目前并非所有浏览器都支持async/await。下面是一个有用的表格,用于检查此功能和其他功能: