我从E6 Promises开始。我非常喜欢它们,但有一个关于错误处理的关键概念,我不理解,并且希望得到一些澄清。
让我们假设以下简单的函数返回一个promise:
function promiseString(str, timeout, doResolve) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (doResolve) {
resolve(str);
} else {
reject(new Error("Rejecting " + str));
}
}, timeout);
});
}
它非常简单,只返回传递给它的字符串的promise,并导致在“timeout”毫秒内解析或拒绝该promise(基于第三个参数)。
我可以完全按照以下方式使用它:
promiseString("One", 100, true)
.then((str) => { console.log("First then is " + str); return promiseString(str + " two", 100, true); })
.then((str) => { console.log("Second then is " + str); return promiseString(str + " three", 100, true); })
.then((str) => console.log(str))
.catch((err) => console.error(err));
如果在此链中的任何调用中将第三个参数从“true”更改为“false”,则会按预期捕获我的错误并发送到console.error()。
然而,现在想象一下构建一个有前途的对象的以下(同样愚蠢的)函数:
function DoublePromiser(str1, str2, doResolve) {
this.promise = new Promise((resolve, reject) => {
promiseString(str1, 100, doResolve)
.then((s1) => promiseString(s1 + str2, 100, doResolve))
.then((s2) => resolve(s2));
});
}
现在想象一下,我按照以下方式使用此代码,一切都解决,没有任何拒绝,(doResolve设置为true):
var dp = new DoublePromiser("Big", "Promise", true);
dp.promise
.then((s) => console.log("DoublePromise: " + s))
.catch((err)=>console.log("I did catch: ", err.message));
正如所料,我在控制台中看到以下内容:
DoublePromise:BigPromise
但是,现在我改变消费代码,将doResolve设置为“false”(这会导致我的承诺例程拒绝):
var dp = new DoublePromiser("Big", "Promise", false);
dp.promise
.then((s) => console.log("DoublePromise: " + s))
.catch((err)=>console.log("I did catch: ", err.message));
由于我对错误应该“冒泡”的理解,我希望控制台记录如下:
我抓住了:拒绝大
但事实并非如此。相反,控制台显示未被捕获的错误:
未捕获(承诺)错误:拒绝大
如果我在DoublePromiser的链末尾添加一个catch,我只能得到我所期望的(和欲望),如下所示:
function DoublePromiser(str1, str2, doResolve) {
this.promise = new Promise((resolve, reject) => {
promiseString(str1, 100, doResolve)
.then((s1) => promiseString(s1 + str2, 100, doResolve))
.then((s2) => resolve(s2))
.catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK
});
}
现在我得到了我的期望,错误并没有被捕获。但这似乎与错误冒出的整个想法背道而驰,并且为了重新拒绝相同的错误而抓住错误似乎很奇怪。
我是否错过了一种简单的工作方式?
我错过了一些基本概念吗?
答案 0 :(得分:4)
您正在使用promise构造函数反模式。不要在你做出的另一个承诺中包含一个现有的承诺,因为这会让你做很多额外的工作来使事情正常工作,而且因为大多数人都没有做好这些额外的工作,所以也很容易出现编程错误。只要回复你已经拥有的承诺。
改变这个:
function DoublePromiser(str1, str2, doResolve) {
this.promise = new Promise((resolve, reject) => {
promiseString(str1, 100, doResolve)
.then((s1) => promiseString(s1 + str2, 100, doResolve))
.then((s2) => resolve(s2))
.catch((err) => reject(err)); // ADDING THIS TO MAKE IT WORK
});
}
到此:
function DoublePromiser(str1, str2, doResolve) {
return promiseString(str1, 100, doResolve)
.then((s1) => promiseString(s1 + str2, 100, doResolve));
}
然后,只需将其用作函数:
DoublePromiser("Big", "Promise", false).then(...);
回顾:您几乎总是希望从.then()
处理程序中返回内部承诺,因为这样可以使嵌套错误向上传播,也可以正确地链/序列异步操作。
并且,您希望避免在现有承诺中包含新承诺,因为您可以直接链接到/和/或返回您已有的承诺。
此外,请注意,如果您执行.catch()
,那么将会处理"被拒绝的承诺并返回一个新的未被拒绝的承诺,从那里继续承诺链,除非在.catch()
处理程序内返回被拒绝的承诺或抛出异常。所以,这个:
p.catch((err) => console.log(err)).then(() => console.log("chain continues"))
很乐意做两个console.log()
语句,因为.catch()
"处理了"承诺让承诺链继续愉快。
正如我在之前的评论中所说,这些100%的理论讨论很难得到你真正想要完成的事情(我们必须猜出真正的问题是什么),而没有关于承诺如何工作的20页教程很多东西。如果你发布一个你试图用这种技术解决的现实世界的问题会更好,我们可以用几行代码和几段解释来展示/解释这样做的最佳方法