Promise高级版 - 通过输出题理解「Promise源码」

1 Promise源码分析

Promise的基本工作原理
Promise构造函数:Promise构造函数接受一个执行器函数作为参数,该函数有两个参数:resolvereject。在构造函数内部,会创建一个Promise实例,并初始化其状态为pending。

状态转换:当执行器函数调用resolve函数时,Promise的状态会变为fulfilled;当调用reject函数时,状态会变为rejected。同时,resolve和reject函数会触发回调函数的执行。

回调函数队列:Promise内部维护一个回调函数队列,用于存储注册的回调函数。当Promise的状态已经变为fulfilledrejected时,回调函数会立即执行;如果Promise的状态还是pending,回调函数会被添加到回调函数队列中,等待状态变化时执行。

then()方法:then()方法用于注册成功回调函数,它接受两个参数:onFulfilledonRejected。当Promise的状态已经变为fulfilled时,会执行onFulfilled回调函数;当状态变为rejected时,会执行onRejected回调函数。then()方法返回一个新的Promise实例,可以实现链式调用。

catch()方法:catch()方法用于注册失败回调函数,它只接受一个参数:onRejected。它实际上是then()方法的一种特殊形式,相当于调用then(undefined, onRejected)

class Promise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
                this.onResolvedCallbacks.forEach((callback) => callback());
            }
        };

        const reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach((callback) => callback());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
        onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
            throw reason;
        };

        const promise2 = new Promise((resolve, reject) => {

            // Promise的queueMicrotask方法是用于将一个微任务(microtask)添加到微任务队列中。

            if (this.state === 'fulfilled') {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                });
            }

            if (this.state === 'rejected') {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                });
            }

            if (this.state === 'pending') {
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });

                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }
        });

        return promise2;
    }

    catch(onRejected) {
        return this.then(null, onRejected);
    }
}


/**
 * resolvePromise是Promise的内部方法之一,用于处理Promise的解析过程。
 * 它接受三个参数:promise(要解析的Promise实例)、x(解析值)、resolve(用于解析Promise的回调函数)。
 * resolvePromise的作用是根据解析值x的类型,决定如何处理Promise的解析过程。它遵循Promise/A+规范中的规则,根据不同的情况执行相应的操作。
*/
function resolvePromise(promise, x, resolve, reject) {
    if (promise === x) {
        // 如果promise和解析值x是同一个对象,抛出TypeError
        return reject(new TypeError('Chaining cycle detected for promise'));
    }

    if (x instanceof Promise) {
        // 如果解析值x是一个Promise实例,等待其状态变为fulfilled或rejected,然后继续解析
        x.then(resolve, reject);
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        let called = false;

        try {
            const then = x.then;

            if (typeof then === 'function') {
                then.call(
                    x,
                    (y) => {
                        if (called) return;
                        called = true;
                        resolvePromise(promise, y, resolve, reject);
                    },
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                );
            } else {
                // 如果解析值x是一个普通对象或函数,将Promise状态设置为fulfilled,并传递解析值x
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            // 如果获取then方法抛出异常,将Promise状态设置为rejected,并传递异常信息
            reject(error);
        }
    } else {
        // 如果解析值x是一个原始值,将Promise状态设置为fulfilled,并传递解析值x
        resolve(x);
    }
}

2 题目背景

这一期还是@洛千陨 珍藏题,带着疑问去看Promise源码:为什么’6’比’hello’先打印?

const p1 = () => (new Promise((resolve, reject) => {
    console.log(1);
    resolve(6);
    let p2 = new Promise((resolve, reject) => {
     console.log(2);
     const timeOut1 = setTimeout(() => {
      console.log(3);
      resolve(4);
     }, 0)
     resolve(5);
    });
    p2.then((arg) => {
     console.log(arg);
     return 'hello'
    }).then((value) => {
        console.log(value)
    });
   
   }));
   const timeOut2 = setTimeout(() => {
    console.log(8);
    const p3 = new Promise(reject => {
     reject(9);
    }).then(res => {
     console.log(res)
    })
   }, 0)
   
   
   p1().then((arg) => {
    console.log(arg);
   });
   console.log(10);
输出结果:
1 2 10 5 6 hello 8 9 3

输出结果分析

  • 疑问:为什么’6’比’hello’先打印?
    答: 参照源码,promise的then方法里面会返回一个promise。
    ①p2第一个then里的代码推入微任务队列(第一个),p1.then里的代码推入微任务队列(第二个);
    ②现在来执行第一个微任务打印'5',return 'hello’会把p2第二个then里的代码推入微任务队列(第三个);
    ③执行第二个微任务打印'6'
    ④执行第三个微任务打印'hello'
  • 输出结果的具体分析可以看另一篇博客的题目(1):ECMAScript 6 - 通过输出题理解「Promise」

你可能感兴趣的:(前端,js,ES6,javascript,es6)