【问题标题】:What happens if I reject a Promise with another Promise value?如果我拒绝具有另一个 Promise 值的 Promise,会发生什么?
【发布时间】:2017-01-04 23:37:10
【问题描述】:

如果 Promise p 使用 Promise(或 Thenable)q 的值解析,则它本质上成为 Promise q 的副本。如果q 被解析,p 将被解析为相同的值。

Promise.resolve(Promise.resolve("hello"));
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}

如果q 被拒绝,p 将被拒绝并使用相同的值。

Promise.resolve(Promise.reject(new Error("goodbye")));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: goodbye}

Promise p 通过 Promise q 解决/拒绝,而不是直接使用相应的值这一事实与最终结果无关。中间 Promise 作为解析过程的一部分被消耗,并且对消费者不可见。

如果q 是从未解决或拒绝的,p 也将永远保持等待状态。

Promise.resolve(new Promise(() => null)); // perpetually-pending promise
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

这些情况众所周知,但我从未见过如果一个 Promise 被另一个 Promise 值rejected(而不是解决)会发生什么。拒绝过程是否也消耗了中间的 Promise,还是原封不动地通过了?

如果它确实消耗了它们,那它是如何工作的?

【问题讨论】:

  • 这并不难测试自己,但我前几天试图快速查找这种行为,在搜索中找不到答案,所以我决定发帖。
  • 不确定问题是什么?
  • @guest271314:没有。帖子里有三个带问号的句子。阅读它们。没有问题,他只是想了解会发生什么。
  • 我从未见过会发生什么” - 有充分的理由。 A Promise.reject message should always be wrapped in an Error.
  • "如果 q 被解析,p 将被解析并具有相同的值。"是一个陷阱!它应为“如果 q 满足,p 将 满足 具有相同的值”。

标签: javascript promise


【解决方案1】:

让我们看看如果我们用已解决的 Promise q 拒绝 Promise p 会发生什么:

Promise.reject(Promise.resolve("hello"));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Promise {
    [[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}

或更明确地说:

const q = Promise.resolve("hello");
const p = Promise.reject(q);
p.then(null, x => console.log("Rejection value:", x));
Rejection value: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}

Promise q,拒绝值,永远不会被解包! p 的拒绝处理程序是用 Promise q 本身调用的,而不是它包含的值。 p>

这也意味着p 的拒绝处理程序不需要等待q 被解析后才能运行。即使q 永远无法解决,p 的拒绝处理程序仍然可以被调用。

Promise.reject(new Promise(() => null)); // Reject with perpetually-pending Promise
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Promise {
    [[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

最后,让我们使用另一个被拒绝的 Promise q 来确认拒绝 Promise p 的行为:

Promise.reject(Promise.reject(new Error("goodbye")));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Error: goodbye(…)(anonymous function)
Uncaught (in promise) Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: goodbye}

我们再次看到 q 没有解包,p 的拒绝处理程序将使用 q 本身调用,而不是 q 已被拒绝的值。

【讨论】:

  • @guest271314 这个答案展示了所提问题的答案。
  • @guest271314 也许。如果您无法理解 - 请提出另一个问题来为您解释这一问题。 (我们需要更深入)
  • @guest271314 - 这里的结论是,给Promise.reject() 一个被拒绝的承诺只是通过被拒绝的承诺。因此,任何.catch() 处理程序将看到的拒绝reason 将是实际被拒绝的承诺,即reason。这似乎与 Promise.resolve() 不同,后者将解开你给它的任何承诺。
  • @guest271314 - Unwrap 意味着它不只是返回承诺,而是看到您正在指定一个承诺,因此它会查看该承诺的内部并使用该承诺的值。展开的一个常见示例是当您从 .then() 处理程序返回一个承诺时。 .then() 处理程序不只是通过父承诺的已解决值来做出承诺。相反,它接受该承诺并将其链接到父承诺。这通常称为展开。
  • @guest271314 - 是的,我在这里做了类似的事情:jsfiddle.net/jfriend00/xbqp2toj
【解决方案2】:

Promise 对象构造:

    new Promise( executor)

使用两个回调函数参数调用执行器函数:

   executor( resolve, reject)

resolve 被参数类型重载到任一位置

  1. 链接承诺到thenable

    resolve( thenable);  // link the resolved promise to the final state of the thenable.
    

    已解决的 Promise 一直处于待处理状态,直到它链接到的 Promise(“被解决”)被解决,或者

  2. 用不是thenable的东西来实现承诺

      resolve (notThenable);  // fulfill the promise with anything except a thenable.
    

作为 javascript,在此上下文中的“重载”是通过在运行时而不是在编译脚本时检查 resolve 的参数类型和属性来执行的。 一个简单的解释是,你不能用一个 promise 或 promise like object 来实现一个 promise。

reject 函数没有重载,也不会在运行时检查其参数。如果一个 Promise 被一个 Promise 或其他 thenable 拒绝,它被视为任何其他拒绝原因。承诺不会保持未决状态并被拒绝。用于拒绝的承诺作为原因参数传递给任何捕获承诺拒绝的函数。 一个简单的解释是,你可以用任何你喜欢的东西来拒绝一个承诺,但如果它不是 Error 对象或描述性原因,你就只能靠你自己了!

【讨论】:

    【解决方案3】:

    所以,杰里米的回答解释了会发生什么:

    const p = Promise.reject(Promise.resolve(3)); 
    

    p 是一个被拒绝的承诺,Promise 的拒绝值为 3。

    我们被教导要相信承诺永远不会用承诺解决!嗯,这是一个特例。在这里,我们用另一个与 then 的做法相矛盾的承诺拒绝了一个承诺。

    但是为什么?!?

    轻松的运动。让我们先了解一些术语。

    promise 以 pending 开始,它可以变成:

    • fulfilled - 用一个值标记它已完成。
    • rejected - 将其标记为失败并说明原因。

    到目前为止一切都很好,但让我们考虑两个额外的术语:

    • resolved - 表示它已解析为另一个 Promise 值并正在跟踪它。
    • 已解决 - 表示它实际上已实现或被拒绝 - 无论是通过它所遵循的承诺解决还是自行解决。

    呸。现在已经不碍事了:

    Promise.resolve 所做的是创建一个承诺已解决 到另一个值。如果该值是一个承诺,它会跟踪它 - 否则它会立即使用传入的值解决。如果您在 thenawait 中的 async 函数中的某个东西中 return 也会发生这种情况。

    Promise.reject 所做的是创建一个带有另一个值的承诺rejected。它没有机会遵循另一个承诺,因为它是立即创建的,结果被拒绝。

    此行为在rejectresolve 中指定。特别是 - 我们正在创建 promise capabilityresolve is special - 即查看“Promise Resolve Functions”。

    好的,你告诉我发生了什么 - 但是为什么?!?!?!?

    好吧,让我们考虑一下替代方案。我们希望resolve 模仿从then 中返回或在async 函数中等待,而rejectthenasync 函数中模仿throwing。

    const p = Promise.resolve().then(() => {
        throw Promise.reject(5);
    });
    

    更清楚地看到将p 解析为 5 毫无意义!我们会将承诺标记为正确完成,但它显然没有正确完成。

    同样:

    异步函数 foo() { 抛出 Promise.resolve(5); } 富(); // 没有人会期望 foo 解决。

    用未包装的值拒绝呢?

    这意味着我们会丢失有关我们正在处理的拒绝的信息。唯一的选择是用 Promise 对象拒绝。

    我应该遇到这种情况吗?

    不,从来没有。无论如何,你永远不应该throw 承诺,你应该永远reject with Errors

    【讨论】:

    • “我们丢失了关于我们正在处理的拒绝的信息”——我不确定我是否理解你的意思,或者也许这很重要。在履行情况下,无论最终值嵌套多深,它仍然作为感兴趣的实际值展开(跟踪更准确)。为什么要拒​​绝遵循不同的逻辑?我唯一能想到的是 throw 应该会导致拒绝,即使你 throw 一个 pending 承诺;即,一个正常的承诺永远不会解决是可以的,但无论抛出什么,throw 都应该激活错误处理。
    • @GabrielL。因为拒绝表示完成值只是值的错误。错误意味着您的代码可能存在错误,此时您真的想要所有错误以及每个错误的堆栈跟踪。
    • 啊,这很有道理。当你将它们与它们试图模拟的同步 try-catch-finally 行为联系起来时,就像 promise 的大多数方面所做的那样。谢谢! …PS,是的,总是throw 有错误,绝对。我询问了这个极端案例,因为我写了一个符合 A+ 标准的 Promise 库是为了好玩,但现在我正在采取额外的步骤来匹配 ES Promise。
    【解决方案4】:

    Promise.resolve() 可以接受一个值、一个 thenable 或一个 promise。它会调整自己的行为。

    Promise.reject() 只取一个立即数。因此,如果您将 Promise 传递给它,它会笨拙地尝试将其视为立即值。

    但是,您不会通过将 Promise 传递给 Promise.reject 来使用它。你可以这样做:

    Promise.reject(myPromise); // weird and useless, and with no side effect
    myPromise.then(e=>{console.log(e)}); // "consume" the promise
    myPromise.then(e=>{console.log(e)}); // you can consume it as many times as you want
    

    【讨论】:

      猜你喜欢
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2018-05-15
      • 1970-01-01
      • 1970-01-01
      • 2018-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多