我读过几篇关于这个主题的文章,但我仍然不清楚《承诺》和《承诺》之间是否有区别。拒绝与抛出错误。例如,

使用Promise.reject

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,因为它更短,但我想知道其中一种比另一种是否有任何优势。


当前回答

区别在于三元运算符

你可以使用

return condition ? someData : Promise.reject(new Error('not OK'))

你不能用

return condition ? someData  : throw new Error('not OK')

其他回答

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
} 

不太好,这正是承诺。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.
      }

区别在于三元运算符

你可以使用

return condition ? someData : Promise.reject(new Error('not OK'))

你不能用

return condition ? someData  : throw new Error('not OK')

有一个区别——这应该不重要——其他答案都没有涉及到,所以:

如果传递给then抛出的实现处理程序,则调用then返回的承诺连同抛出的内容一起被拒绝。

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

然而,任何依赖于这种差异的代码都从根本上被破坏了。:-)它不应该对承诺和解的时间那么敏感。

这里有一个例子:

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));

如果你运行它,在写这篇文章时,你会得到:

Error from usingThrow: 2 is not 42!
Error from usingReject: 1 is not 42!

注意顺序。

将其与相同的链进行比较,但都使用usingThrow:

函数usingThrow(val) { 返回Promise.resolve (val) .then(v => { If (v !== 42) { 抛出新的错误(' ${v}不是42! '); } 返回v; }); } usingThrow (1) .then(v => console.log(v)) .catch(e =>控制台。error(" error from usingThrow:", e.message)); usingThrow (2) .then(v => console.log(v)) .catch(e =>控制台。error(" error from usingThrow:", e.message));

这表明拒绝处理程序以另一种顺序运行:

Error from usingThrow: 1 is not 42!
Error from usingThrow: 2 is not 42!

我在上面说“可能”,是因为在其他领域已经有一些工作,在其他类似的情况下,如果所有涉及的承诺都是本机承诺(而不仅仅是thenable),那么就去掉了这个不必要的额外标记。(具体来说:在一个异步函数中,return await x最初引入了一个额外的async tick,而return x在其他方面是相同的;ES2020改变了它,如果x是原生承诺,在没有其他差异的地方,额外的勾将被删除。)

同样,任何对承诺的结算时间如此敏感的代码都已经被破坏了。所以这真的不重要。

实际上,正如其他回答所提到的:

正如Kevin B指出的,如果你在回调到你在实现处理程序中使用的其他函数中,throw将不起作用——这是一个大问题 正如lukyer指出的那样,throw会突然终止函数,这可能很有用(但在示例中使用的是return,它做的是同样的事情) 正如venator指出的那样,你不能在条件表达式(?:),至少现在还没有

除此之外,这主要是风格/偏好的问题,所以对于大多数情况,你要与你的团队达成一致(或者你不在乎任何一种方式),并保持一致。

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

例子:

new Promise((resolve, reject) => { 把“犯错”; console.log(“从来没有达到”); }) .then(() => console.log("RESOLVED")) .catch(() => console.log("REJECTED"));

vs

new Promise((resolve, reject) => { 拒绝();// resolve()行为类似 console.log(“总是达到“);// "REJECTED"将在此之后打印 }) .then(() => console.log("RESOLVED")) .catch(() => console.log("REJECTED"));

一个要尝试的例子。只需将isVersionThrow更改为false以使用reject而不是throw。

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