es6 Promise源码解析,建议直接运行打断点观察

170 阅读6分钟

问题一: 如何做到状态变化后执行后续的then中的代码的?

promise构造函数中传入异步代码块:

new promise((res) =>{resolve(res)}).then(value => {console.info(value)}) new Promise()本身是同步的,后面跟的then也是同步的。但是new promise传入的箭头函数中的代码可能是异步的,这就是说有可能then已经执行了,但是new promise中传入的箭头函数没有执行完成,有没有发现,这有点违反我们的直觉, 然而事实却是这样的:首先根据事件循环原理(eventloop)同步执行的代码肯定优先一步到底执行完毕,异步代码稍后执行,所以then并不会等待前面new Promise构造函数中传入的异步箭头函数,then中传入的函数会根据当前Promise的状态【包含三种:pending(进行中)、fulfilled(已成功)和rejected(已失败)】来分别执行不同的操作,首先如果是pending状态的话,then中的函数会直接放入到预备数组中进行缓存(不会执行),当new promise中传入的异步函数执行完成后并调用了resolve()或者reject()方法之后 Promise的状态随之改为了fulfilled或者rejected,与此同时,之前存入对应的预备数组中的函数会被取出进行执行。补充一点,then中可传入两个函数,分别为resolve调用之后对应的回调函数和reject调用之后对应的回调。如果当前的Promise的状态为pending,then中的resolve回调函数和reject回调分别放入两个预备数组中,后面会根据promise构造函数中传入的异步代码块包含resolve方法或者是reject方法来判断是取哪个预备数组中的函数来执行。 Promise构造函数中如果resolve()存在于同步代码块中,那么Promise的状态在执行then之前就会是fulfilled

promise的class类

// promise 原理代码
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
// 接收执行器:也就是说在new MyPromise((resolve, reject)=>{resolve("成功")})的时候,传匿名函数
// executor会马上进行同步执行,而匿名函数中又传入了resolve,并且在匿名函数中进行了传参执行
    constructor(executor) { 
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e);
        }
    }

status = PENDING; // promise的状态
value = undefined; // 成功之后的值
reason = undefined; // 失败之后的值
successCallback = []; // 成功回调
failCallback = []; // 失败回调

// resolve和reject为什么要用箭头函数?
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 用箭头函数就可以让this指向当前实例对象
resolve = value => {
    // 如果状态不是等待 组织程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    //this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) this.successCallback.shift()()
}
reject = reason => {
    // 如果状态不是等待 组织程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) this.failCallback.shift()();
}

then (successCallback, failCallback) {
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => {
        throw reason
    };   
    // then链式调用参数变为可选参数 例:promise.then().then().then((value)=>{console.log(value)}) // value;
    //这里new MyPromise传入的匿名function是会执行的,构造函数里面会将传入的function直接执行了
    let promise2 = new MyPromise((resolve, reject) => {
        // 传递一个执行器 立马执行
        // 判断状态
        if (this.status === FULFILLED) {
            setTimeout(() => { // 将下面的代码变成异步代码
                try {
                    let x = successCallback(this.value)
                    // 判断 x 的值是普通值还是promise对象
                    // 如果是普通值 直接调用resolve
                    // 如果是promise对象 查看promise对象返回的结果
                    // 再根据promise对象返回的结果 界定调用resolve 还是reject
                    resolvePromise(x, resolve, reject);
                } catch (e) { // 处理异常状态
                    reject(e)
                }
            }, 0)
        } else if (this.status === REJECTED) {
            setTimeout(() => { // 将下面的代码变成异步代码
                try {
                    let x = failCallback(this.reason)
                    resolvePromise(x, resolve, reject);
                } catch (e) { // 处理异常状态
                    reject(e)
                }
            }, 0)
        } else {
            // 当前状态是等待
            // 将成功回调和失败回调存储起来 - 多个then方法调用
            this.successCallback.push(() => {
                setTimeout(() => { // 将下面的代码变成异步代码
                    try {
                        let x = successCallback(this.value);
                        resolvePromise(x, resolve, reject);
                    } catch (e) { // 处理异常状态
                        reject(e)
                    }
                }, 0)
            });
            this.failCallback.push(() => {
                setTimeout(() => { // 将下面的代码变成异步代码
                    try {
                        let x = failCallback(this.reason);
                        resolvePromise(x, resolve, reject);
                    } catch (e) { // 处理异常状态
                        reject(e)
                    }
                }, 0)
            });

        }
    });
    return promise2;
}

finally (callback) {
    /**
     - 无论当前最终状态是成功还是失败,finally都会执行
     - 我们可以在finally方法之后调用then方法拿到结果
     - 这个函数是在原型对象上用的
     * **/
    return this.then(value => {
        return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
        return MyPromise.resolve(callback()).then(() => {
            throw reason
        })
    })
}

catch (failCallback) {
    /**
     - catch方法是为了捕获promise对象的所有错误回调的
     - 直接调用then方法,然后成功的地方传递undefined,错误的地方传递reason
     - catch方法是作用在原型对象上的方法
     * **/
    return this.then(undefined, failCallback);
}

static all (array) {
    /**
     * 分析一下:
     - all方法接收一个数组,数组中可以是普通值也可以是promise对象
     - 数组中值得顺序一定是我们得到的结果的顺序
     - promise返回值也是一个promise对象,可以调用then方法
     - 如果数组中所有值是成功的,那么then里面就是成功回调,如果有一个值是失败的,那么then里面就是失败的
     - 使用all方法是用类直接调用,那么all一定是一个静态方法
     **/
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
        function addData (key, value) {
            // 保存每个传入参数执行完毕,并存储返回值
            result[key] = value;
            index++;
            if (index === array.length) {
                resolve(result)
            }
        }

        for (let i = 0; i < array.length; i++) {
            let current = array[i];
            if (current instanceof MyPromise) {
                // promise对象
                current.then(value => addData(i, value), reason => reject(reason))
            } else {
                // 普通值
                addData(i, array[i])
            }
        }
    })
}

static resolve (value) {
    /**
     resolve方法的作用是将给定的值转换为promise对象 resolve的返回值的promise对象
     如果参数就是一个promise对象,直接返回,如果是一个值,那么需要生成一个promise对象,把值进行返回
     是Promise类的一个静态方法
     * **/
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value))
}
}

function resolvePromise (x, resolve, reject) {
    if (x instanceof MyPromise) {
        // promise 对象
        // x.then( value => resolve(value),reason => reject(reason))
        x.then(resolve, reject)  // 简化
    } else {
        // 普通值
        resolve(x)
    }
}

module.exports = MyPromise;

测试类:

import {MyPromise} from "./MyPromise"
// new的时候传入的箭头函数会直接同步执行
let promise = new MyPromise((resolve,reject)=>{
    // setTimeout(()=>{ // 异步操作
    //     resolve('成功')
    // },2000)
    resolve('成功')
})
let p1 = promise.then(value =>{
    console.log(value)
    return p1
})

// 测试 then方法可以多次调用
// then 方法的链式调用 1.实现链式调用 2.将一个返回值作为第二个then的参数
promise.then(value => {
    console.log(value);
    return p1
},reason => {
    console.log(reason)
}).then(value => {
    console.log(value)
})
// 打印结果-> 成功 成功