Promise V8 一源码分析------------引用

            本文基于 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. }

 

  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>;

大意是说处于 rejected 状态的 Promise 必须要有处理函数。V8 通过 HasHandler 判断 myPromise1 并没有处理函数。当把处理函数加上以后,代码如下:

 

  1. const myPromise1 = new Promise((resolve, reject) => {

  2. reject()

  3. })

  4.  

  5. myPromise1.then(console.log, console.log)

此时 SetHasHandler 已被调用,HasHandler 返回 true 表示 myPromise1 有处理函数。在 node-v14.13.0 环境下执行,没有错误提示,一切正常

1.3 其它
  • executor:是函数,Promise 构造函数接收的参数

  • PromiseReaction:是对象,表示 Promise 的处理函数,因为一个 Promise 多次调用 then 方法就会有多个处理函数,所以底层数据结构是个链表。

2.构造函数

构造函数源码如下

  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. }

首先分析两个 ThrowTypeError,以下代码可触发第一个 ThrowTypeError。

  1. Promise() // Uncaught TypeError: undefined is not a promise

原因是没有使用 new 操作符调用 Promise 构造函数,此时 newTarget 等于 Undefined,触发了 ThrowTypeError(MessageTemplate::kNotAPromise, newTarget)。

以下代码可触发第二个 ThrowTypeError

  1. 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。

  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 构造函数接收的参数 executor,是被同步调用的

Promise 构造函数调用 NewJSPromise 获取一个新的 JSPromise 对象。NewJSPromise 调用 PromiseInit 来初始化一个 JSPromise 对象,源码如下

 

  1. macro PromiseInit(promise: JSPromise): void {

  2. promise.reactions_or_result = kZero;

  3. promise.flags = SmiTag(JSPromiseFlags{

  4. status: PromiseState::kPending,

  5. has_handler: false,

  6. handled_hint: false,

  7. async_task_id: 0

  8. });

  9. promise_internal::ZeroOutEmbedderOffsets(promise);

  10.  

3.then

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

 

posted @ 2020-10-15 19:23  又回到了起点  阅读(274)  评论(0编辑  收藏  举报