本文基于 node 版本 14.13.0,V8 版本 8.4.371。Promise 源码全部位于 V8
1.基本数据结构
Promise 共有 3 种状态,源码如下
1. // Promise constants
2. extern enum PromiseState extends int31 constexpr 'Promise::PromiseState' {
3. kPending,
4. kFulfilled,
5. kRejected
6. }
一个新创建的 Promise 处于 pending 状态。当调用 resolve 或 reject 函数后,Promise 处于 fulfilled 或 rejected 状态,此后 Promise 的状态保持不变,也就是说 Promise 的状态改变是不可逆的,Promise 源码中出现了多处状态相关的 assert。
1.2 JSPromise
JSPromise 描述 Promise 的基本信息,源码如下:
1. bitfield struct JSPromiseFlags extends uint31 {
2. status: PromiseState: 2 bit; // Promise 的状态,kPending/kFulfilled/kRejected
3. has_handler: bool: 1 bit; // 是否有处理函数,没有调用过 then 方法的 Promise 没有处理函数
4. handled_hint: bool: 1 bit;
5. async_task_id: int32: 22 bit;
6. }
大意是说处于 rejected 状态的 Promise 必须要有处理函数。V8 通过 HasHandler 判断 myPromise1 并没有处理函数。当把处理函数加上以后,代码如下:
1. @generateCppClass
2. extern class JSPromise extends JSObject {
3. macro Status(): PromiseState {
4. // 获取 Promise 的状态,返回 kPending/kFulfilled/kRejected 中的一个
5. return this.flags.status;
6. }
7.
8. macro SetStatus(status: constexpr PromiseState): void {
9. // 第 1 个 assert 表示只有 pending 状态的 Promise 才可以被改变状态
10. assert(this.Status() == PromiseState::kPending);
11. // 第 2 个 assert 表示 Promise 创建成功后,不可将 Promise 设置为 pending 状态
12. assert(status != PromiseState::kPending);
13. this.flags.status = status;
14. }
15.
16. macro HasHandler(): bool {
17. // 判断 Promise 是否有处理函数
18. return this.flags.has_handler;
19. }
20.
21. macro SetHasHandler(): void {
22. this.flags.has_handler = true;
23. }
24.
25. // Smi 0 terminated list of PromiseReaction objects in case the JSPromise was
26. // not settled yet, otherwise the result.
27. // promise 处理函数或结果,可以是无/包装了 onFulfilled/onRejected 回调函数的对象/resolve 接收的参数
28. reactions_or_result: Zero|PromiseReaction|JSAny;
29. flags: SmiTagged<JSPromiseFlags>;
此时 SetHasHandler 已被调用,HasHandler 返回 true 表示 myPromise1 有处理函数。在 node-v14.13.0 环境下执行,没有错误提示,一切正常
1. const myPromise1 = new Promise((resolve, reject) => {
2. reject()
3. })
4.
5. myPromise1.then(console.log, console.log)
1.3 其它
- executor:是函数,Promise 构造函数接收的参数
- PromiseReaction:是对象,表示 Promise 的处理函数,因为一个 Promise 多次调用 then 方法就会有多个处理函数,所以底层数据结构是个链表。
2.构造函数
构造函数源码如下
首先分析两个 ThrowTypeError,以下代码可触发第一个 ThrowTypeError。
1. / https://tc39.es/ecma262/#sec-promise-executor
2. transitioning javascript builtin
3. PromiseConstructor(
4. js-implicit context: NativeContext, receiver: JSAny,
5. newTarget: JSAny)(executor: JSAny): JSAny {
6. // 1. If NewTarget is undefined, throw a TypeError exception.
7. if (newTarget == Undefined) {
8. ThrowTypeError(MessageTemplate::kNotAPromise, newTarget);
9. }
10.
11. // 2. If IsCallable(executor) is false, throw a TypeError exception.
12. if (!Is<Callable>(executor)) {
13. ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor);
14. }
15.
16. let result: JSPromise;
17. // 构造一个 Promise 对象
18. result = NewJSPromise();
19. // 从 Promise 对象 result 身上,获取它的 resolve 和 reject 函数
20. const funcs = CreatePromiseResolvingFunctions(result, True, context);
21. const resolve = funcs.resolve;
22. const reject = funcs.reject;
23. try {
24. // 直接同步调用 executor 函数,resolve 和 reject 做为参数
25. Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject);
26. } catch (e) {
27. Call(context, reject, Undefined, e);
28. }
29. return result;
30. }
-
Promise() // Uncaught TypeError: undefined is not a promise
原因是没有使用 new 操作符调用 Promise 构造函数,此时 newTarget 等于 Undefined,触发了 ThrowTypeError(MessageTemplate::kNotAPromise, newTarget)。
以下代码可触发第二个 ThrowTypeError
-
new Promise() // Uncaught TypeError: Promise resolver undefined is not a function
此时 newTarget 不等于 Undefined,不会触发第一个 ThrowTypeError。但调用 Promise 构造函数时没传参数 executor,触发了第二个 ThrowTypeError。
错误消息在 C++ 代码中定义,使用了宏和枚举巧妙的生成了 C++ 代码,这里不做展开,源码如下
1. T(NotAPromise, "% is not a promise") \
2. T(ResolverNotAFunction, "Promise resolver % is not a function") \
executor 的类型是函数,在 JavaScript 的世界里,回调函数通常是异步调用,但 executor 是同步调用。在 Call(context, UnsafeCast(executor), Undefined, resolve, reject) 这一行,同步调用了 executor。
Promise 构造函数接收的参数 executor,是被同步调用的
1. console.log('同步执行开始')
2. new Promise((resolve, reject) => {
3. resolve()
4. console.log('executor 同步执行')
5. })
6.
7. console.log('同步执行结束')
8. // 本段代码的打印顺序是:
9. // 同步执行开始
10. // executor 同步执行
11. // 同步执行结束
Promise 构造函数调用 NewJSPromise 获取一个新的 JSPromise 对象。NewJSPromise 调用 PromiseInit 来初始化一个 JSPromise 对象,源码如下
3.then
1.
2. macro PromiseInit(promise: JSPromise): void {
3. promise.reactions_or_result = kZero;
4. promise.flags = SmiTag(JSPromiseFlags{
5. status: PromiseState::kPending,
6. has_handler: false,
7. handled_hint: false,
8. async_task_id: 0
9. });
10. promise_internal::ZeroOutEmbedderOffsets(promise);
3.1 PromisePrototypeThen
JavaScript 层的 then 函数实际上是 V8 中的 PromisePrototypeThen 函数
1. transitioning javascript builtin
2. PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
3. onFulfilled: JSAny, onRejected: JSAny): JSAny {
4. // 1. Let promise be the this value.
5. // 2. If IsPromise(promise) is false, throw a TypeError exception.
6. const promise = Cast<JSPromise>(receiver) otherwise ThrowTypeError(
7. MessageTemplate::kIncompatibleMethodReceiver, 'Promise.prototype.then',
8. receiver);
9.
10. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
11. const promiseFun = UnsafeCast<JSFunction>(
12. context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
13.
14. // 4. Let resultCapability be ? NewPromiseCapability(C).
15. let resultPromiseOrCapability: JSPromise|PromiseCapability;
16. let resultPromise: JSAny;
17. label AllocateAndInit {
18. const resultJSPromise = NewJSPromise(promise);
19. resultPromiseOrCapability = resultJSPromise;
20. resultPromise = resultJSPromise;
21. }
22. // onFulfilled 和 onRejected 是 then 接收的两个参数
23. const onFulfilled = CastOrDefault<Callable>(onFulfilled, Undefined);
24. const onRejected = CastOrDefault<Callable>(onRejected, Undefined);
25.
26. // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
27. // resultCapability).
28. PerformPromiseThenImpl(
29. promise, onFulfilled, onRejected, resultPromiseOrCapability);
30. // 返回一个新的 Promise
31. return resultPromise;
32. }
PromisePrototypeThen 函数创建了一个新的 Promise,获取 then 接收到的两个参数,调用 PerformPromiseThenImpl 完成大部分工作。这里有一点值得注意,then 方法返回的是一个新创建的 Promise。
1. const myPromise2 = new Promise((resolve, reject) => {
2. resolve('foo')
3. })
4.
5. const myPromise3 = myPromise2.then(console.log)
6.
7. // myPromise2 和 myPromise3 是两个不同的对象,有不同的状态和不同的处理函数
8. console.log(myPromise2 === myPromise3) // 打印 false
then 方法返回的是一个新的 Promise