Nar*_*esh 329 javascript promise
我已经阅读了几篇关于这个主题的文章,但我仍然不清楚是否Promise.reject
与抛出错误之间存在差异.例如,
使用Promise.reject
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
return Promise.reject(new PermissionDenied());
}
});
Run Code Online (Sandbox Code Playgroud)
用投掷
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
throw new PermissionDenied();
}
});
Run Code Online (Sandbox Code Playgroud)
我的偏好是throw
仅仅因为它更短而使用,但是想知道一个是否有任何优势.
use*_*654 284
使用一个与另一个没有任何优势,但是,有一个特定的情况throw
将无法工作.但是,这些案件可以修复.
每当你进入一个承诺回调,你就可以使用throw
.但是,如果您在任何其他异步回调中,则必须使用reject
.
例如,
new Promise(function() {
setTimeout(function() {
throw 'or nah';
// return Promise.reject('or nah'); also won't work
}, 1000);
}).catch(function(e) {
console.log(e); // doesn't happen
});
Run Code Online (Sandbox Code Playgroud)
不会触发捕获,而是留下未解决的承诺和未捕获的异常.这是您想要改为使用的情况reject
.但是,你可以通过宣告超时来解决这个问题:
function timeout(duration) { // Thanks joews
return new Promise(function(resolve) {
setTimeout(resolve, duration);
});
}
timeout(1000).then(function() {
throw 'worky!';
// return Promise.reject('worky'); also works
}).catch(function(e) {
console.log(e); // 'worky!'
});
Run Code Online (Sandbox Code Playgroud)
luk*_*yer 180
另一个重要的事实是,reject()
DOES NOT像终止控制流return
语句一样.相反throw
,终止控制流程.
例:
new Promise((resolve, reject) => {
throw "err";
console.log("NEVER REACHED");
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
Run Code Online (Sandbox Code Playgroud)
VS
new Promise((resolve, reject) => {
reject(); // resolve() behaves similarly
console.log("ALWAYS REACHED"); // "REJECTED" will print AFTER this
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
Run Code Online (Sandbox Code Playgroud)
小智 46
是的,最大的区别是reject是一个回调函数,它在promise被拒绝后执行,而throw不能异步使用.如果您选择使用reject,您的代码将继续以异步方式正常运行,而throw将优先完成解析器功能(此功能将立即运行).
我见过的一个例子帮助我澄清了问题,你可以设置一个带有拒绝的超时功能,例如:
new Promise(_, reject) {
setTimeout(reject, 3000);
});
Run Code Online (Sandbox Code Playgroud)
以上可能无法用throw写.
在你的小例子中,差异无法区分,但在处理更复杂的异步概念时,两者之间的差异可能是极大的.
max*_*ell 38
TLDR: 当函数有时返回一个promise并且有时会引发异常时,它很难使用.在编写异步函数时,更喜欢通过返回被拒绝的promise来表示失败
您的特定示例模糊了它们之间的一些重要区别:
因为你的错误处理内一个承诺链,抛出的异常都会自动转换到拒绝承诺.这可以解释为什么它们似乎可以互换 - 它们不是.
考虑以下情况:
checkCredentials = () => {
let idToken = localStorage.getItem('some token');
if ( idToken ) {
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
} else {
throw new Error('No Token Found In Local Storage')
}
}
Run Code Online (Sandbox Code Playgroud)
这将是一种反模式,因为您需要同时支持异步和同步错误情况.它可能看起来像:
try {
function onFulfilled() { ... do the rest of your logic }
function onRejected() { // handle async failure - like network timeout }
checkCredentials(x).then(onFulfilled, onRejected);
} catch (e) {
// Error('No Token Found In Local Storage')
// handle synchronous failure
}
Run Code Online (Sandbox Code Playgroud)
不好,这里正是在Promise.reject
全球范围内可用的地方(并在全球范围内提供)并有效区分自己throw
.重构现在变成:
checkCredentials = () => {
let idToken = localStorage.getItem('some_token');
if (!idToken) {
return Promise.reject('No Token Found In Local Storage')
}
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以只使用一个catch()
用于网络故障,并使用缺少令牌的同步错误检查:
checkCredentials()
.catch((error) => if ( error == 'No Token' ) {
// do no token modal
} else if ( error === 400 ) {
// do not authorized modal. etc.
}
Run Code Online (Sandbox Code Playgroud)
T.J*_*der 11
有一个区别 - 这应该无关紧要 - 其他答案没有涉及,所以:
如果执行处理程序传递给then
throws,则该调用返回的承诺将then
被抛出的内容拒绝。
如果它返回一个被拒绝的承诺,调用返回的承诺将then
被解析为该承诺(并且最终将被拒绝,因为它被解析为的承诺被拒绝),这可能会引入一个额外的异步“滴答”(在微任务队列,用浏览器术语来说)。
但是,任何依赖于这种差异的代码都从根本上被破坏了。:-) 它不应该对承诺结算的时间那么敏感。
下面是一个例子:
function usingThrow(val) {
return Promise.resolve(val)
.then(v => {
if (v !== 42) {
throw new Error(`${v} is not 42!`);
}
return v;
});
}
function usingReject(val) {
return Promise.resolve(val)
.then(v => {
if (v !== 42) {
return Promise.reject(new Error(`${v} is not 42!`));
}
return v;
});
}
// The rejection handler on this chain may be called **after** the
// rejection handler on the following chain
usingReject(1)
.then(v => console.log(v))
.catch(e => console.error("Error from usingReject:", e.message));
// The rejection handler on this chain may be called **before** the
// rejection handler on the preceding chain
usingThrow(2)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));
Run Code Online (Sandbox Code Playgroud)
如果你运行它,在撰写本文时,你会得到:
使用抛出错误:2 不是 42! usingReject 错误:1 不是 42!
注意顺序。
将其与相同的链进行比较,但都使用usingThrow
:
Error from usingThrow: 2 is not 42! Error from usingReject: 1 is not 42!
这表明拒绝处理程序以其他顺序运行:
使用抛出错误:1 不是 42! 使用抛出错误:2 不是 42!
我在上面说“可能”是因为在其他领域有一些工作在其他类似情况下删除了这种不必要的额外勾选,如果所有涉及的承诺都是本机承诺(不仅仅是 thenables)。(具体来说:在一个async
函数中,return await x
最初引入了额外的异步滴答,return x
而其他方面相同;ES2020 对其进行了更改,以便如果x
是本机承诺,则在没有其他区别的情况下删除额外的滴答。)
同样,任何对承诺的结算时间如此敏感的代码都已经被破坏了。所以真的不重要/不应该重要。
实际上,正如其他答案所提到的:
throw
如果您正在回调到您在履行处理程序中使用的某些其他函数,则将无法工作——这是最大的问题throw
突然终止该功能,这可能很有用(但您return
在示例中使用了相同的功能)throw
在条件表达式 ( ? :
) 中使用,至少现在不能除此之外,这主要是风格/偏好的问题,因此与大多数风格/偏好一样,与您的团队达成一致(或者您不关心任何一种方式),并保持一致。
一个例子来尝试。只需将isVersionThrow更改为false即可使用拒绝而不是引发。
const isVersionThrow = true
class TestClass {
async testFunction () {
if (isVersionThrow) {
console.log('Throw version')
throw new Error('Fail!')
} else {
console.log('Reject version')
return new Promise((resolve, reject) => {
reject(new Error('Fail!'))
})
}
}
}
const test = async () => {
const test = new TestClass()
try {
var response = await test.testFunction()
return response
} catch (error) {
console.log('ERROR RETURNED')
throw error
}
}
test()
.then(result => {
console.log('result: ' + result)
})
.catch(error => {
console.log('error: ' + error)
})
Run Code Online (Sandbox Code Playgroud)
区别在于三元运算符
return condition ? someData : Promise.reject(new Error('not OK'))
Run Code Online (Sandbox Code Playgroud)
return condition ? someData : throw new Error('not OK')
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
161059 次 |
最近记录: |