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

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


当前回答

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

我看到的一个例子帮助我澄清了这个问题,你可以用reject设置Timeout函数,例如:

新承诺(兑现,兑现)=> setTimeout() = >{拒绝(“err味精);控制台日志。(‘吃’)的,1000); 回收回路(“后悔”) }) 然后(o) => console.log(“RESOLVED”,o) .catch(o) => console.log(“reject”,o);

上面的代码不能用throw来写。

尝试{ new Promise((resolve, reject) => { setTimeout(()=>{抛出新的错误('err msg')}, 1000); 返回resolve('ret val') }) .then((o) => console.log("RESOLVED", o)) .catch((o) => console.log("REJECTED", o)); }捕捉(o) { console.log(“忽略”,o) }

在OP的小例子中,差异是难以区分的,但当处理更复杂的异步概念时,两者之间的差异可能是巨大的。

其他回答

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

我看到的一个例子帮助我澄清了这个问题,你可以用reject设置Timeout函数,例如:

新承诺(兑现,兑现)=> setTimeout() = >{拒绝(“err味精);控制台日志。(‘吃’)的,1000); 回收回路(“后悔”) }) 然后(o) => console.log(“RESOLVED”,o) .catch(o) => console.log(“reject”,o);

上面的代码不能用throw来写。

尝试{ new Promise((resolve, reject) => { setTimeout(()=>{抛出新的错误('err msg')}, 1000); 返回resolve('ret val') }) .then((o) => console.log("RESOLVED", o)) .catch((o) => console.log("REJECTED", o)); }捕捉(o) { console.log(“忽略”,o) }

在OP的小例子中,差异是难以区分的,但当处理更复杂的异步概念时,两者之间的差异可能是巨大的。

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

如果传递给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"));

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

使用一种方法与另一种方法相比没有任何优势,但是,在特定的情况下,throw将不起作用。然而,这些情况是可以解决的。

任何时候在promise回调中,都可以使用throw。然而,如果你在任何其他异步回调中,你必须使用拒绝。

例如,这不会触发catch:

新承诺(函数(){ setTimeout(函数(){ 抛出'or nah'; // return承诺。拒绝(或不);也不管用 }, 1000); }) .catch(函数(e) { console.log (e);//不会发生 });

相反,留给您的是一个未解决的承诺和一个未捕获的异常。在这种情况下,您应该使用reject。但是,您可以通过两种方式解决这个问题。

通过在超时内使用原来的Promise的拒绝函数:

new Promise(function(resolve, reject) { setTimeout(function() { 拒绝(“或否”); }, 1000); }).catch(function(e) { 控制台.log(e);工程! });

通过承诺超时:

function timeout(duration){//谢谢joews 返回新的承诺(函数(resolve) { setTimeout(解决,持续时间); }); } 超时(1000)(函数(){ 把“成功!”; // return Promise.reject('work ');同样适用 }) .catch(函数(e) { console.log (e);/ /“成功!” });