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

浙公网安备 33010602011771号