JavaScript承诺-拒绝与抛出

JavaScript承诺-拒绝与抛出,javascript,promise,Javascript,Promise,我已经读了几篇关于这个主题的文章,但我仍然不清楚承诺.拒绝和抛出错误之间是否有区别。比如说, 使用承诺。拒绝 return asyncIsPermitted() .then(function(result) { if (result === true) { return true; } else { return Promise.reject(new PermissionDenied());

我已经读了几篇关于这个主题的文章,但我仍然不清楚
承诺.拒绝
和抛出错误之间是否有区别。比如说,

使用承诺。拒绝

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

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

我更喜欢使用
throw
,只是因为它比较短,但我想知道其中一个是否比另一个有任何优势。

是的,最大的区别是reject是在拒绝承诺后执行的回调函数,而throw不能异步使用。如果选择使用reject,代码将继续以异步方式正常运行,而throw将优先完成解析器函数(此函数将立即运行)

我看到的一个有助于澄清问题的示例是,您可以使用reject设置超时函数,例如:

新承诺((解决、拒绝)=>{
setTimeout(()=>{reject('err msg');console.log('finished')},1000);
返回解析('ret val')
})
.然后((o)=>console.log(“已解决”,o))

.catch((o)=>console.log(“拒绝”,o))使用一个与另一个相比没有任何优势,但是,有一种特殊情况下,
throw
不起作用。但是,这些情况是可以解决的


任何时候当您在promise回调中时,都可以使用
throw
。但是,如果您在任何其他异步回调中,则必须使用
reject

例如,这不会触发捕获:

新承诺(函数(){
setTimeout(函数(){
扔'或不';
//返回承诺。拒绝('or nah');也不起作用
}, 1000);
}).catch(函数(e){
console.log(e);//不发生

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

例如:

新承诺((解决、拒绝)=>{
抛出“错误”;
控制台日志(“从未到达”);
})
。然后(()=>console.log(“已解决”))

.catch(()=>console.log(“拒绝”)TLDR:当函数有时返回承诺,有时抛出异常时,它很难使用。编写异步函数时,最好通过返回拒绝的承诺来表示失败

您的特定示例混淆了它们之间的一些重要区别:

因为您在承诺链中处理错误,所以抛出的异常会自动转换为拒绝的承诺。这也许可以解释为什么它们看起来是可互换的——它们不是

考虑以下情况:

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')
    }
  }
这将是一种反模式,因为您需要同时支持异步和同步错误情况。它可能看起来像:

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
} 
不太好,这里正是
承诺.拒绝
(可在全球范围内获得)拯救的地方,它与
投掷
有着明显的区别。重构现在变成:

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()
来检查网络故障缺少令牌的同步错误检查:

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

一个可以尝试的例子。只需将IsVersionRow更改为false,即可使用拒绝而不是抛出

const IsVersionRow=true
类TestClass{
异步测试函数(){
如果(IsVersionRow){
console.log('Throw version')
抛出新错误('Fail!')
}否则{
console.log('拒绝版本')
返回新承诺((解决、拒绝)=>{
拒绝(新错误('Fail!'))
})
}
}
}
常量测试=异步()=>{
const test=new TestClass()
试一试{
var response=wait test.testFunction()
返回响应
}捕获(错误){
console.log('返回错误')
抛出错误
}  
}
测试()
。然后(结果=>{
console.log('result:'+result)
})
.catch(错误=>{
console.log('错误:'+错误)

})
差异是三元运算符

  • 你可以用
返回条件?someData:Promise.reject(新错误('notok'))
  • 你不能用
返回条件?someData:抛出新错误('not OK')

有一个区别——这不重要——其他答案没有涉及,因此:

如果将履行处理程序传递给
then
抛出,则调用
then
返回的承诺将被拒绝,并包含抛出的内容

如果它返回一个被拒绝的承诺,则调用
返回的承诺将被解析为该承诺(并且最终将被拒绝,因为它解析为的承诺将被拒绝),这可能会引入一个额外的异步“勾号”(微任务队列中的一个循环,用浏览器术语来说)

但是,任何依赖于这种差异的代码都会从根本上被破坏。:-)它不应该对承诺和解的时间如此敏感

下面是一个例子:

使用行(val)的函数{ 返回承诺。解决(val) .然后(v=>{ 如果(v!==42){ 抛出新错误(`${v}不是42!`); } 返回v; }); } 使用拒绝(val)的函数{ 返回承诺。解决(val) .然后(v=>{ 如果(v!==42){ return Promise.reject(新错误(`${v}不是42!`); } 返回v; }); } //此链上的拒绝处理程序可以在**后调用** //以下链上的拒绝处理程序 使用拒绝(1) .then(v=>console.log(v)) .catch(e=>console.error(“使用拒绝的错误:”,e.message)); //此链上的拒绝处理程序可以在**之前调用** //前一个链上的拒绝处理程序 使用行(2) .then(v=>console.log(v)) .catch(e=>console.error(“使用行时出错:”,e.message))这两种方法都会生成