Promise.resolve() 的魔鬼细节

如果我们有:

const foo = Promise.resolve(1);

则最终我们从 foo 中得到的是 1 这个值。

如果像下面这样,从 bar 中能得到的是什么:

const foo = Promise.resolve(1);
const bar = Promise.resolve(foo);

其实得到的也是 1MDN[1] 文档中有相关解释:

The Promise.resolve() method returns a Promise object that is resolved with a given value. If the value is a promise, that promise is returned.

注意后面那句,如果传给 resolve() 的参数是一个 promise,则直接返回那个 promise

由此,我们知道上例中 foobar 引用的其实是同一个对象:

const foo = Promise.resolve(1);
const bar = Promise.resolve(foo);

// true
console.log(foo === bar);

1有意思的地方

那么,Promise.resolve() 怎么判断传入的参数是一个 promise ?

如果你修改了 foo.constructor 属性,会发现 foo 就不再等于 bar 了(SOURCE[2]):

const foo = Promise.resolve(1);
foo.constructor = {};
const bar = Promise.resolve(foo);

// false
console.log(foo === bar);

所以我们猜测它判断参数是否是 promise 依据的是参数的 constructor 属性。

(你可能能会说为什么不用 instanceof 来判断,因为改变 constructor 属性,instanceof 仍然是返回 true,所以用它判断是不可靠的)。

另外,Object.setPrototypeOf() 也能达到修改 constructor 的效果,这就有了另一个有意思的用法:a Promise of a Promise[3]

2A promise of a promise

既然 resolve() 入参如果是 Promise 对象,会直接被返回出去。我们比较叛逆,想实现一个传入的是 Promise,resolved 的值也是 Promise,怎么搞?

先试试下面这种写法(改 construcotr属性):

const foo = Promise.resolve(1);
foo.constructor = {};

const bar = Promise.resolve(foo);

// false
console.log(foo === bar);

// 1 false
bar.then(v => console.log(v, bar === foo));

行不通,resolved 的值是 1,我们想要的是 vfoo 引用的是同一个对象(都是Promise)。

再试试这种写法:

const foo = Promise.resolve(1);
Object.setPrototypeOf(foo, null);

const bar = Promise.resolve(foo);

// false
console.log(foo === bar);

// true
bar.then(v => console.log(v === foo));

成功。

3注意

这样写代码并没有什么意思,而且真要把 vPromise 来用,还得加一句:

Object.setPrototypeOf(v, Promise.prototype);

以恢复 v 的原型链。

不过本文的主要用意,是让你了解 Promise.resolve()方法以及关注对象的 constructor 属性在某些方面的用途。

要留心的是,Promise.reject() 并不遵从相同的范式。

4总结

对于 Promise.resolve(),如果传参是一个 promise,则会直接返回该 promise;但如果修改了参数的 constructor 属性,它会返回一个新的 promise(只是引用不同,最终 resolved 值是相同的,可以认为该参数变成了一个 thenable);如果修改了参数的 prototype,则 resolved 值直接变为参数本身(如同传的不是 promise 一样)。

参考资料

[1]

MDN: Promise.resolve(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

[2]

Promise.resolve() makes decisions based on the .constructor property of its argument: https://twitter.com/addaleax/status/1382039026883047428

[3]

A Promise of a Promise: https://twitter.com/JFieldEffectT/status/1382077150002569218


原文始发于微信公众号(背井):Promise.resolve() 的魔鬼细节

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/246659.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!