从 JS 中被拒绝的承诺中恢复
Recovering from rejected promises in JS
我正在使用本机承诺(大部分)并尝试从错误中恢复并继续执行承诺链。
实际上,我正在这样做:
- REST 查询以查看 ID 是否存在。 请注意,这 return 是 jquery 延迟的。
- .then(成功表示ID存在,失败停止)
(失败表示ID不存在,继续创建ID)
- .then(创建ID记录并发送到服务器)
I return a Promise.resolve() 来自我被拒绝的函数,这应该会导致下一个 .then 的成功部分执行。它不是。我已经在 Chrome 和 Safari 上试过了。
请注意,第一个承诺实际上是一个延迟查询,但根据此页面 (http://api.jquery.com/deferred.then/),deferred.then() return 是一个承诺对象。所以添加一个额外的 .then 应该隐藏到原生的承诺。
为了更清楚 - 这是伪代码:
promise = $.ajax(url);
promise = promise.then(); // convert to promise
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
.then(function() { createIdentityDetails(); });
.then(function() { sendIdentityDetails(); });
请注意,我想在 ajax return 成功时失败,我想
当 ajax 调用失败时继续处理。
发生的是所有后续 .then 部分执行的 FAIL 函数。也就是说,我的 return Promise.resolve() 不起作用 - 这(我认为)违反了规范。
对于如何处理长承诺链中的错误并从错误中恢复的任何反馈,我将不胜感激。
非常感谢您提供的任何建议。
p.s。创建和收集完整的身份信息非常耗时,所以如果 ID 存在,我不想这样做。所以我想先检查,然后快速失败。
p.p.s 我真的很喜欢 promises 解开这些深度嵌套的异步回调链的方式。
希望这会把事情弄清楚一点,你有几个流浪者;并且你正在做你不需要在 then 函数中做的事情
首先,我确定你不想要
promise = promise.then();
行,代码如下所示
promise = $.ajax(url);
promise.then(function() {
cleanup();
throw 'success is an error'; // this is equivalent to return Promise.reject('success is an error');
}, function(err) {
return 'failure is good'; // returning here means you've nullified the rejection
}) // remove the ; you had on this line
.then(function() { createIdentityDetails(); }) // remove the ; on this line
.then(function() { sendIdentityDetails(); }) // remove the ; on this line
.catch(function(err) { }); // you want to catch the error thrown by success
似乎 JQuery 承诺不允许您将失败变为成功。但是,如果您使用本机承诺,则可以。
例如:
Promise.resolve()
.then(function() {console.log("First success"); return Promise.reject(); },
function() { console.log("First fail"); return Promise.resolve(); })
.then(function() {console.log("Second success"); return Promise.reject(); },
function() { console.log("Second fail"); return Promise.resolve(); })
.then(function() {console.log("Third success"); return Promise.reject(); },
function() { console.log("Third fail"); return Promise.resolve(); })
这里我 return 来自第一个成功处理程序的拒绝。在第二个失败处理程序中,我 return 解决了。这一切都按预期工作。输出为 (Chrome):
First success
Second fail
Third success
事实证明 正确的 处理 jQuery deferred 和 promises 的方法是转换它们:
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
(来自 http://www.html5rocks.com/en/tutorials/es6/promises/)。
这很好用,所以如果您将上面的初始行更改为:
Promise.resolve($.ajax("this will fail"))
...
你正确地得到:
First fail
Second success
Third fail
底线...投延期以尽快承诺,然后一切似乎都正常。
promise = promise.then(); // convert to promise
嗯? $.ajax
返回的承诺已经是承诺。
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
这个问题是 jQuery 不是 Promises/A+ compatible, and fails to adopt promises/thenable from other implementations than its own. You would have to use $.Deferred
here 来完成这个工作,比如
promise.then(function() { cleanup(); return $.Deferred().reject(); },
function() { return $.when(); }); // or return $.Deferred().resolve();
That is, my return Promise.resolve()
doesn't work - which is (I think) in violation of the spec.
确实如此。但是,jQuery is known for this,他们要到 v3.0 才会修复它。
要使您要使用的本机 Promise
库正常工作,您需要避免使用 jQuery 的 then
。这个:
var $promise = $.ajax(url);
var promise = Promise.resolve($promise); // convert to proper promise
promise.then(function() {
cleanup();
throw undefined;
}, function(err) {
return undefined;
})
.then(createIdentityDetails)
.then(sendIdentityDetails);
假设 createIdentityDetails()
和 sendIdentityDetails()
是 promise-returning 异步函数...
如果我们在问题中看到的是整个承诺链,那么处理错误情况就很简单。没有必要将成功转化为失败或将失败转化为成功,或者从一种承诺转变为另一种承诺。
$.ajax(url).then(function() {
cleanup();
}, function(err) {
createIdentityDetails()
.then(sendIdentityDetails);
});
无论 return 由 createIdentityDetails()
jQuery 或非 jQuery.
编辑的承诺类型如何,这都将起作用
但是,如果还有更多内容,例如需要将结果告知调用函数,那么您需要做更多的事情,这取决于您希望如何报告可能的结果。
报告 'ID already exists' 失败,'new ID created' 报告成功
问题是这样的
function foo() {
return $.ajax(url).then(function() {
cleanup();
return $.Deferred().reject('failure: ID already exists');
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
将两种类型的结果都报告为成功
这似乎更明智,因为处理的错误将被报告为成功。只有不可预测的、未处理的错误才会被报告。
function foo() {
return $.ajax(url).then(function() {
cleanup();
return 'success: ID already exists';
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
无论采用哪种报告策略,承诺类型createIdentityDetails()
return都非常重要。作为链中的第一个承诺,它决定了它所链接的 .thens 的行为。
- 如果
createIdentityDetails()
return 是原生 ES6 承诺,那么不用担心,大多数风格的承诺,甚至 jQuery 都会被同化。
- 如果
createIdentityDetails()
return 有一个 jQuery 承诺,那么只有 jQuery 个承诺会被同化。因此 sendIdentityDetails()
还必须 return 一个 jQuery promise(或者一个 ES6 promise 必须用 $.Deferred(...)
重铸成 jQuery),最终成功的转换器(如上编码)。
您可以通过这两种方式看到混合 jQuery 和 ES6 promise 的效果 here。第一个警报由第二个代码块生成,不是预期的。第二个警报由第一个块生成并正确给出结果 98 + 1 + 1 = 100.
我正在使用本机承诺(大部分)并尝试从错误中恢复并继续执行承诺链。
实际上,我正在这样做:
- REST 查询以查看 ID 是否存在。 请注意,这 return 是 jquery 延迟的。
- .then(成功表示ID存在,失败停止) (失败表示ID不存在,继续创建ID)
- .then(创建ID记录并发送到服务器)
I return a Promise.resolve() 来自我被拒绝的函数,这应该会导致下一个 .then 的成功部分执行。它不是。我已经在 Chrome 和 Safari 上试过了。
请注意,第一个承诺实际上是一个延迟查询,但根据此页面 (http://api.jquery.com/deferred.then/),deferred.then() return 是一个承诺对象。所以添加一个额外的 .then 应该隐藏到原生的承诺。
为了更清楚 - 这是伪代码:
promise = $.ajax(url);
promise = promise.then(); // convert to promise
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
.then(function() { createIdentityDetails(); });
.then(function() { sendIdentityDetails(); });
请注意,我想在 ajax return 成功时失败,我想 当 ajax 调用失败时继续处理。
发生的是所有后续 .then 部分执行的 FAIL 函数。也就是说,我的 return Promise.resolve() 不起作用 - 这(我认为)违反了规范。
对于如何处理长承诺链中的错误并从错误中恢复的任何反馈,我将不胜感激。
非常感谢您提供的任何建议。
p.s。创建和收集完整的身份信息非常耗时,所以如果 ID 存在,我不想这样做。所以我想先检查,然后快速失败。
p.p.s 我真的很喜欢 promises 解开这些深度嵌套的异步回调链的方式。
希望这会把事情弄清楚一点,你有几个流浪者;并且你正在做你不需要在 then 函数中做的事情
首先,我确定你不想要
promise = promise.then();
行,代码如下所示
promise = $.ajax(url);
promise.then(function() {
cleanup();
throw 'success is an error'; // this is equivalent to return Promise.reject('success is an error');
}, function(err) {
return 'failure is good'; // returning here means you've nullified the rejection
}) // remove the ; you had on this line
.then(function() { createIdentityDetails(); }) // remove the ; on this line
.then(function() { sendIdentityDetails(); }) // remove the ; on this line
.catch(function(err) { }); // you want to catch the error thrown by success
似乎 JQuery 承诺不允许您将失败变为成功。但是,如果您使用本机承诺,则可以。
例如:
Promise.resolve()
.then(function() {console.log("First success"); return Promise.reject(); },
function() { console.log("First fail"); return Promise.resolve(); })
.then(function() {console.log("Second success"); return Promise.reject(); },
function() { console.log("Second fail"); return Promise.resolve(); })
.then(function() {console.log("Third success"); return Promise.reject(); },
function() { console.log("Third fail"); return Promise.resolve(); })
这里我 return 来自第一个成功处理程序的拒绝。在第二个失败处理程序中,我 return 解决了。这一切都按预期工作。输出为 (Chrome):
First success
Second fail
Third success
事实证明 正确的 处理 jQuery deferred 和 promises 的方法是转换它们:
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
(来自 http://www.html5rocks.com/en/tutorials/es6/promises/)。 这很好用,所以如果您将上面的初始行更改为:
Promise.resolve($.ajax("this will fail"))
...
你正确地得到:
First fail
Second success
Third fail
底线...投延期以尽快承诺,然后一切似乎都正常。
promise = promise.then(); // convert to promise
嗯? $.ajax
返回的承诺已经是承诺。
promise.then(function() { cleanup(); return Promise.reject(); }, function(err) { return Promise.resolve(); });
这个问题是 jQuery 不是 Promises/A+ compatible, and fails to adopt promises/thenable from other implementations than its own. You would have to use $.Deferred
here 来完成这个工作,比如
promise.then(function() { cleanup(); return $.Deferred().reject(); },
function() { return $.when(); }); // or return $.Deferred().resolve();
That is, my
return Promise.resolve()
doesn't work - which is (I think) in violation of the spec.
确实如此。但是,jQuery is known for this,他们要到 v3.0 才会修复它。
要使您要使用的本机 Promise
库正常工作,您需要避免使用 jQuery 的 then
。这个
var $promise = $.ajax(url);
var promise = Promise.resolve($promise); // convert to proper promise
promise.then(function() {
cleanup();
throw undefined;
}, function(err) {
return undefined;
})
.then(createIdentityDetails)
.then(sendIdentityDetails);
假设 createIdentityDetails()
和 sendIdentityDetails()
是 promise-returning 异步函数...
如果我们在问题中看到的是整个承诺链,那么处理错误情况就很简单。没有必要将成功转化为失败或将失败转化为成功,或者从一种承诺转变为另一种承诺。
$.ajax(url).then(function() {
cleanup();
}, function(err) {
createIdentityDetails()
.then(sendIdentityDetails);
});
无论 return 由 createIdentityDetails()
jQuery 或非 jQuery.
但是,如果还有更多内容,例如需要将结果告知调用函数,那么您需要做更多的事情,这取决于您希望如何报告可能的结果。
报告 'ID already exists' 失败,'new ID created' 报告成功
问题是这样的
function foo() {
return $.ajax(url).then(function() {
cleanup();
return $.Deferred().reject('failure: ID already exists');
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
将两种类型的结果都报告为成功
这似乎更明智,因为处理的错误将被报告为成功。只有不可预测的、未处理的错误才会被报告。
function foo() {
return $.ajax(url).then(function() {
cleanup();
return 'success: ID already exists';
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
无论采用哪种报告策略,承诺类型createIdentityDetails()
return都非常重要。作为链中的第一个承诺,它决定了它所链接的 .thens 的行为。
- 如果
createIdentityDetails()
return 是原生 ES6 承诺,那么不用担心,大多数风格的承诺,甚至 jQuery 都会被同化。 - 如果
createIdentityDetails()
return 有一个 jQuery 承诺,那么只有 jQuery 个承诺会被同化。因此sendIdentityDetails()
还必须 return 一个 jQuery promise(或者一个 ES6 promise 必须用$.Deferred(...)
重铸成 jQuery),最终成功的转换器(如上编码)。
您可以通过这两种方式看到混合 jQuery 和 ES6 promise 的效果 here。第一个警报由第二个代码块生成,不是预期的。第二个警报由第一个块生成并正确给出结果 98 + 1 + 1 = 100.