JavaScript的承诺 – 拒绝与抛出

我已经阅读了关于这个主题的几篇文章,但是我还不清楚Promise.reject和抛出一个错误是否有区别。 例如,

使用Promise.reject

 return asyncIsPermitted() .then(function(result) { if (result === true) { return true; } else { return Promise.reject(new PermissionDenied()); } }); 

使用throw

 return asyncIsPermitted() .then(function(result) { if (result === true) { return true; } else { throw new PermissionDenied(); } }); 

我的select是使用throw因为它更短,但想知道是否有优势。

使用一个和另一个没有什么优势,但是,有一个特殊的情况下throw不行。 但是,这些情况可以修复。

任何时候你在一个承诺callback里面,你都可以使用throw 。 但是,如果您使用其他asynchronouscallback,则必须使用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 }); 

另一个重要的事实是, reject() 不会return语句那样终止控制stream。 相反, throw会终止控制stream。

例:

 new Promise((resolve, reject) => { throw "err"; console.log("NEVER REACHED"); }) .then(() => console.log("RESOLVED")) .catch(() => console.log("REJECTED")); 

是的,最大的不同在于, 拒绝是一个callback函数,在承诺被拒绝后执行,而throw不能asynchronous使用。 如果您select使用拒绝,您的代码将继续以asynchronous方式正常运行,而throw将优先完成parsing器function(此函数将立即运行)。

我见过的一个例子帮助我澄清了这个问题,就是您可以用拒绝来设置Timeout函数,例如:

 new Promise(_, reject) { setTimeout(reject, 3000); }); 

以上可能无法用throw来写。

在你的小例子中,差别是难以区分的,但是当处理更复杂的asynchronous概念时,两者之间的差异可能是激烈的。

TLDR: 当函数有时返回一个promise,有时会抛出一个exception,这个函数很难使用。 在编写asynchronous函数时,更愿意通过返回被拒绝的承诺来表示失败

您的具体例子混淆了它们之间的一些重要区别:

由于您承诺链进行error handling,因此抛出的exception会自动转换为拒绝的承诺。 这可以解释为什么他们似乎是可以互换的 – 他们不是。

考虑下面的情况:

 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') } } 

这将是一种反模式,因为您需要同时支持asynchronous和同步错误情况。 它可能看起来像这样:

 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 } 

不好,这里是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}` } }) } 

现在,您可以只使用一个catch()来检测networking故障同时检查缺less令牌的同步错误:

 checkCredentials() .catch((error) => if ( error == 'No Token' ) { // do no token modal } else if ( error === 400 ) { // do not authorized modal. etc. }