如果我用另一个Promise值拒绝一个Promise会发生什么?

What happens if I reject a Promise with another Promise value?

本文关键字:Promise 一个 什么 另一个 拒绝 如果      更新时间:2023-09-26

如果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 q解析/拒绝Promise p,而不是直接使用各自的值,这与最终结果无关。中间Promise作为解决过程的一部分被消费,对消费者是不可见的。

如果q是一个从未解决或拒绝,p也将永远处于挂起状态。

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

这些情况是众所周知的,但我从来没有见过如果一个Promise被拒绝(而不是用另一个Promise值解决)会发生什么。拒绝过程是否也消耗中间承诺,或者它们是完整地通过的?

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

让我们看看如果我们用已解析的承诺q拒绝承诺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"}

承诺q,拒绝值,永远不会被打开! p的拒绝处理程序使用承诺q本身,而不是它包含的值来调用。

这也意味着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}

最后,让我们确认如果我们使用另一个被拒绝的承诺q拒绝承诺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被拒绝的值。

Jeremy的回答解释了发生的事情:

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

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

我们被教导要相信承诺,而不是用承诺来解决!这是一个特例。在这里,我们用另一个与then相矛盾的承诺来拒绝一个承诺。

为什么? ! ?

放松点。让我们先了解一些术语。

承诺以开头,等待,它可以变成:

  • completed -用值标记完成。
  • 已拒绝 -标记失败并给出原因。

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

  • resolved -表示已解析到另一个承诺值,并正在跟踪它。
  • 已解决 -意味着它实际上已经完成或被拒绝-通过它所解决的承诺或它自己。

唷。现在,这是出路:

Promise.resolve的作用是创建一个promise ,将解析为另一个值。如果该值是一个承诺,它会跟踪它,否则它会立即处理传入的值。如果在async函数中从thenawait中获取return,也会发生这种情况。

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

此行为在rejectresolve中指定。特别是——我们正在创建一个承诺功能,resolve是特别的——也就是看看"承诺解析函数"。

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

好吧,让我们考虑一下其他选择。我们希望resolve模拟then的返回或async函数中的等待,reject模拟throwthenasync函数中的返回。

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

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

同样:

async函数foo() {把Promise.resolve (5);}foo ();//没有人会期望foo解析。

拒绝unwrapped值呢?

那就意味着我们失去了处理拒绝的信息。唯一的选择是使用Promise对象拒绝。

我应该遇到这种情况吗?

没有,从来没有。无论如何,你都不应该使用throw承诺,而应该使用Error s拒绝。

承诺对象构造:

    new Promise( executor)

使用两个回调函数参数调用executor函数:

   executor( resolve, reject)

其中resolve按参数类型重载为

  1. linkthenable的承诺:

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

    ,在它链接的承诺("was resolved with")被解决之前,已解决的承诺一直处于挂起状态,或者

  2. 用非thenable

    实现的承诺
      resolve (notThenable);  // fulfill the promise with anything except a thenable.
    

作为javascript,在此上下文中的"重载"是由resolve在运行时检查参数类型和属性来执行的,而不是在编译脚本时执行的。一个简单的解释是,你不能用一个承诺或类似承诺的对象来履行一个承诺。

reject函数没有重载,也不会在运行时检查它的参数。如果以承诺或其他thenable拒绝承诺,则将其视为任何其他拒绝理由。承诺不会一直悬而未决,而会被拒绝。用于拒绝的承诺作为reason参数传递给捕获承诺拒绝的任何函数。一个简单的解释是,你可以拒绝任何你喜欢的承诺,但如果它不是一个Error对象或描述性的原因,你就靠自己了!

promise. resolve()可以接受一个值、一个thenable或一个promise。它适应它的行为。

Promise.reject()只接受一个直接值。所以如果你传递一个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