提出问题
Promise对于JavaScript来说太重要了,前端的同学估计没有哪一天不接触Promise,JavaScript的世界里异步无处不在。
Promise解决了回调地狱的问题,还为async await的出现做了垫脚石。
但是大家每次在亲手创造一个Promise对象时,内心是否时常有一丝丝焦虑和憎恨:还要手写回调函数,resolve想传出来还得再定义一个变量,恶心!或许你刚开始是抗拒的,但是后来接受了,习惯了。
这里我们把恶心的代码再次贴出来:
// 多定义了两个变量,只是为了传递值
let resolve, reject;
const promise = new Promise((res, rej) => {
// 还是要写恶心的回调函数
resolve = res;
reject = rej;
});
// 某个地方
resolve(data)
// 或者某个地方
reject(error);
你有没有写过这样恶心的代码?你肯定写过。
解决问题
这次的新特性Promise.withResolvers,就是来解救我们的。
直接看代码示例:
const { promise, resolve, reject } = Promise.withResolvers();
这就创造了一个新的Promise实例,而且把其对应的resolve reject方法也给出来了。
没有回调,不需要另设变量把resolve传出来。
这段代码和上面那段恶心的代码等价,大家对照一下,应该很快就理解了。
不过可能大家会认为Promise.withResolvers这个名字不太合适,有人提出应该叫Promise.create。但是Promise.withResolvers返回的是一个普通对象(POJO),如果叫Promise.create的话,返回的应该是Promise实例更合适。关于名字问题有过很多争论,但是最终也没有定下一个更合适的名字,因此目前为止,还是叫Promise.withResolvers。
浏览器支持情况
该特性目前还在 Stage 3 阶段,预计2024年正式发版。 目前主流浏览器还没有全部支持该特性。
Polyfill
该特性要实现Polyfill并不复杂,core-js 已经有Polyfill了。大家完全可以先用起来。
有意思的特性
MDN上还给出了一个非常有意思的特性,请看示例代码:
class NotPromise {
constructor(executor) {
// executor中传入的两个方法会分别传给返回的`resove`和`reject`变量
executor(
(value) => console.log("Resolved", value),
(reason) => console.log("Rejected", reason),
);
}
}
const { promise, resolve, reject } = Promise.withResolvers.call(NotPromise);
resolve("hello");
// 打印: Resolved hello
例子中通过call方法,用NotPromise替换掉了Promise。
call是函数实例的一个方法,可以改变函数中this的指向,具体call的用法可以看MDN
NotPromise虽然不是Promise类,但是可以模拟Promise构造函数的结构。
Promise构造函数接受一个函数作为参数,在这里对应的就是executor函数,Promise构造函数会去调用这个函数,并传入两个参数,分别是resolve函数和reject函数。
NotPromise只要能模拟上面Promise做的事情,就可以用来代替Promise。只是这时的resove和reject也已经被代替,不再具有Promise的功能。
这里最终返回的resolve和reject分别就是executor函数传入的两个函数参数。
这里最终返回的promise对象是NotPromise的实例。
所以整个代码只是借用了Promise的形式,和Promise的功能本身已经毫无关系了。
目前还看不出这样的功能有什么用处,或许哪天就有用了。
结束语
Promise.withResolvers是一个非常让人兴奋的特性,它颠覆了我们创建Promise的方式。