TLDR: 当函数有时返回Promise有时抛出异常时,它很难使用。编写异步函数时,更喜欢通过返回被拒绝的Promise来表示失败
您的特定示例混淆了它们之间的一些重要区别: 
因为您在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')
    }
  }
这将是一种反模式,因为您将需要同时支持异步和同步错误情况。它可能看起来像:
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()来处理网络故障和缺少令牌的同步错误检查:
checkCredentials()
      .catch((error) => if ( error == 'No Token' ) {
      // do no token modal
      } else if ( error === 400 ) {
      // do not authorized modal. etc.
      }