• 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. macro SetStatus(status: constexpr PromiseState): void {

    8. // 第 1 个 assert 表示只有 pending 状态的 Promise 才可以被改变状态

    9. assert(this.Status() == PromiseState::kPending);

    10. // 第 2 个 assert 表示 Promise 创建成功后,不可将 Promise 设置为 pending 状态

    11. assert(status != PromiseState::kPending);

    12. this.flags.status = status;

    13. }

    14. macro HasHandler(): bool {

    15. // 判断 Promise 是否有处理函数

    16. return this.flags.has_handler;

    17. }

    18. macro SetHasHandler(): void {

    19. this.flags.has_handler = true;

    20. }

    21. // Smi 0 terminated list of PromiseReaction objects in case the JSPromise was

    22. // not settled yet, otherwise the result.

    23. // promise 处理函数或结果,可以是无/包装了 onFulfilled/onRejected 回调函数的对象/resolve 接收的参数

    24. reactions_or_result: Zero|PromiseReaction|JSAny;

    25. flags: SmiTagged<JSPromiseFlags>;

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

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

    2. reject()

    3. })

    4. 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. // 2. If IsCallable(executor) is false, throw a TypeError exception.

    11. if (!Is<Callable>(executor)) {

    12. ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor);

    13. }

    14. let result: JSPromise;

    15. // 构造一个 Promise 对象

    16. result = NewJSPromise();

    17. // 从 Promise 对象 result 身上,获取它的 resolve 和 reject 函数

    18. const funcs = CreatePromiseResolvingFunctions(result, True, context);

    19. const resolve = funcs.resolve;

    20. const reject = funcs.reject;

    21. try {

    22. // 直接同步调用 executor 函数,resolve 和 reject 做为参数

    23. Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject);

    24. } catch (e) {

    25. Call(context, reject, Undefined, e);

    26. }

    27. return result;

    28. }

    首先分析两个 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. console.log('同步执行结束')

    7. // 本段代码的打印顺序是:

    8. // 同步执行开始

    9. // executor 同步执行

    10. // 同步执行结束

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

    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. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).

    10. const promiseFun = UnsafeCast<JSFunction>(

    11. context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);

    12. // 4. Let resultCapability be ? NewPromiseCapability(C).

    13. let resultPromiseOrCapability: JSPromise|PromiseCapability;

    14. let resultPromise: JSAny;

    15. label AllocateAndInit {

    16. const resultJSPromise = NewJSPromise(promise);

    17. resultPromiseOrCapability = resultJSPromise;

    18. resultPromise = resultJSPromise;

    19. }

    20. // onFulfilled 和 onRejected 是 then 接收的两个参数

    21. const onFulfilled = CastOrDefault<Callable>(onFulfilled, Undefined);

    22. const onRejected = CastOrDefault<Callable>(onRejected, Undefined);

    23. // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,

    24. // resultCapability).

    25. PerformPromiseThenImpl(

    26. promise, onFulfilled, onRejected, resultPromiseOrCapability);

    27. // 返回一个新的 Promise

    28. return resultPromise;

    29. }

    PromisePrototypeThen 函数创建了一个新的 Promise,获取 then 接收到的两个参数,调用 PerformPromiseThenImpl 完成大部分工作。这里有一点值得注意,then 方法返回的是一个新创建的 Promise。

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

    2. resolve('foo')

    3. })

    4. const myPromise3 = myPromise2.then(console.log)

    5. // myPromise2 和 myPromise3 是两个不同的对象,有不同的状态和不同的处理函数

    6. console.log(myPromise2 === myPromise3) // 打印 false

           then 方法返回的是一个新的 Promise

  • 相关阅读:
    SorceTree 与 Bitbucket连接
    Android回调事件传播-android学习之旅(四十五)
    USACO Section 2.2 Subset Sums
    多线程计数器——原子操作
    《怪诞经济学》:2星。故意伪装成外国作者和翻译书。作者了解一些经济学结论,但是没受过经济学方面的学术训练。
    《科技失控》:3星。科技可能带给人类的坏的结果的汇总。
    《蚂蚁金服》:3星。支付宝准官修历史。
    《这才是心理学》:心理学常见误解与相关伪科学辨析。反证《巨婴国》作者要么是有意的骗子,要么是心理学差生。5星。
    《哈佛商业评论》2017年第4期:“指导式”销售能将购买便捷度提升86%。4星
    《知识大融通》:在生物、哲学、文化领域旁征博引,但是主题不够明确。3星。
  • 原文地址:https://www.cnblogs.com/zhouyideboke/p/13822407.html
Copyright © 2020-2023  润新知