一般 Node 教科书对 Promise 基本用法的描述是:
const promiseObj = new Promise(function (resolve, reject) {
// ... some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
});
为 Promise 的 constructor 传入一个 executor 函数,在其中有两个改变状态的函数 resolve/reject(也可以不叫这两个名字),在这个 executor 中做完异步操作后,使用 resolve/reject 来改变当前创建的这个 promiseObj 的状态。
但是,「做**异步操作」和「改变 promiseObj **状态」这两件事,都可以不必限制在传给 Promise constructor 的这个 executor 中去做!两件事情,都完全可以在其它地方(如其它的函数、类、package)去做!
而要实现这样的灵活性,其要点就在于把这个 executor 的 resolve/reject 函数给返回出来。此时,executor 本身只需要做一件事:将 resolve/reject 赋值到外部去,即:将 resolve/reject 做成 closure 返回到外部去。
let resolveRef, rejectRef;
const promiseObj = new Promise(function (resolve, reject) {
resolveRef = resolve;
rejectRef = reject;
});
// ....
await promiseObj;
// ... do async operations at other function/class/package...
// ... and after done, call the resolveRef/rejectRef change promiseObj state.
此时,阻塞的地方可以依旧可以直接 await 这个 promiseObj,而异步操作可以在任何其他地方(其它的函数、类、package)去做。只要将上面返回到外部的、promiseObj 的 resolve/reject,传递到这个「其他地方」,在它完成自己的异步操作后,直接执行接收到的 resolve/reject 来改变 promiseObj 状态即可。
可以看到 promiseObj 本身其实是没有 resolve/reject 方法的。Promise constructor 接收到的是一个 executor,而这个executor 的定义是:
- executor 本身是一个函数。
- 这个函数的第一个参数也是一个函数,在「定义处」称之为 resolve。
- 这个函数的第二个参数也是一个函数,在「定义处」称之为 reject。
- Promise 内部是根据这个 executor 的 parameter 定义来做状态的改变,但最后创建出来的实例,并不知晓有方法叫做 resolve/reject。因为这个两个方法是依赖于 executor 而定义的,而不是依赖于 Promise 本身而定义的。
所以,要拿到这个 executor 的 resolve/reject 方法,就只能在定义 executor 的时候获取。也即是上面所说的通过外部定义的 resolveRef, rejectRef 在定义出将它们返回出来。
一旦外部获得了 executor 的 resolve/reject 方法,那么关于这个 promise 的异步执行函数、以及执行完成后对这个 promise 状态的改变,都可以非常灵活地在其它地方发生了。