进阶一Promise源码

1.核心实现

let promise = new Promise((resolve,reject) => {
     
	resolve('ok');
	reject('err')
})
promise.then(
	(value) => {
     console.log(value);},
	(err) => {
     console.log(err);}
)

根据Promise的使用方法得到一些信息:
①Promise就是一个类 在执行这个类的时候 需要传递一个回调函数进去 称之为执行器,执行器会立即执行
② 有三种状态 fulfilled rejected pending
pending -->fulfilled
pending -->rejected
一旦确定就不可以更改了
③resolve和reject函数是用来更改状态的
resolve:fulfilled
reject:rejected
④then方法内部做的事情就是判断状态,成功就调用成功的回调函数,失败就调用失败的回调函数
⑤ then方法中成功回调函数有一个参数表示成功之后的值,then失败函数的参数表示失败的原因

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
     
    constructor(executor){
     
        executor(this.resolve,this.reject)
    }
    
    status = PENDING;//定义初始状态为pending
    value = undefined;//定义成功的值 初始为undefined
    reason = undefined;//定义失败的原因 初始为undefined

    resolve =value => {
     //定义为箭头函数是为了在使用的时候this指向这个类的实例对象 MyPromise
        //如果状态不是pending就阻止向下执行
        if(this.status !== PENDING) return
        // 更改状态
        this.status = FULFILLED;
        //保存成功的值
        this.value = value
    }
    reject = reason => {
     
        //如果状态不是pending就阻止向下执行
        if(this.status !== PENDING) return
        // 更改状态
        this.status = REJECTED;
        //保存失败的原因
        this.reason = reason;
    }
    then (successCallback,failCallback){
     
        if(this.status === FULFILLED){
     
            successCallback(this.value)
        }else if(this.status === REJECTED){
     
            failCallback(this.reason)
        }
    }

}

2.加入异步逻辑
不会马上知道异步到底是成功还是失败,就需要把成功和失败的回调函数保存起来,等待异步执行完之后再执行回调
进阶一Promise源码_第1张图片
3.实现then方法多次调用
如果是同步立即执行就可以
如果是异步需要把回调函数放在数组里保存,然后成功时依次调用
如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
进阶一Promise源码_第2张图片
4.then方法链式调用
后面then方法回调函数拿到的值是上一个then方法回调函数返回的的值
能够链式调用就说明每个then方法都返回一个promise 这样才可以链式调用并且还要将回调返回值传递给下一个then方法的回调
进阶一Promise源码_第3张图片
但是呢如果当返回值是一个promise对象的时候 就不能直接调用resolve(x)了,得先判断promise对象返回的结果再决定调用resolve还是reject
进阶一Promise源码_第4张图片
5.then方法中返回的promise是自己本身的时候会发生promise循环调用报错
先看一下es2015中的promise
进阶一Promise源码_第5张图片
在实现自己的promise的时候也要考虑到这种情况并且能把错误报出来
进阶一Promise源码_第6张图片
6.处理异常
①执行器代码发生错误
进阶一Promise源码_第7张图片
②在then方法的回调函数中发生错误 会在下一个then方法中捕获到
进阶一Promise源码_第8张图片
③完善then方法中失败和等待的状态
几乎和成功状态的代码一样
进阶一Promise源码_第9张图片
在循环调用回调函数的时候就不用传参数了
进阶一Promise源码_第10张图片
7.then方法的参数是可选的
先看下es2015中的promise
进阶一Promise源码_第11张图片
在我们自己实现的promise中也要支持不传参数的情况
在这里插入图片描述
8.all方法
测试代码:

function p1() {
     
            return new MyPromise((resolve, reject) => {
     
                setTimeout(()=>{
     
                    resolve('ok');
                },1000)
            })
        }
        function p2() {
     
            return new MyPromise((resolve, reject) => {
     
                resolve('ok2');
            })
        }
        MyPromise.all(['a','b',p1(),p2(),'c']).then((value)=>{
     console.log(value);
        })

实现及结果:
进阶一Promise源码_第12张图片
最终实现:
使用计数器 判断当前执行的个数是否等于传入参数的个数

static all(array){
     
        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 是的话将promise返回值放入结果中
                    current.then(
                        (value)=>{
     addData(i,value)},
                        (reason)=>{
     reject(reason)}
                    )
                }else{
     
                    addData(i,current)
                }
            }
        })
    }

9.resolve

static resolve(value){
     
        if(value instanceof MyPromise) return value;//如果是promise 直接返回promise对象
        // 如果是普通值 要生成一个promise返回
        return new MyPromise(resolve => resolve(value));
    }

10.finally

无论promise成功还是失败,finally中的回调函数都会被执行一次(所以可以使用.then方法在每个状态中都会执行),并且在finally后可以链式调用then方法得到promise最终返回的值,所以finally要返回一个promise(正好.then方法返回的是一个promise)
进阶一Promise源码_第13张图片

但是当callback返回一个promise时,不会等待promise执行完就return了value
进阶一Promise源码_第14张图片
最终实现:
把callback的返回值转成一个promise(这里就可以用resolve方法了),返回这个promise的返回值,这样就能把callback的返回值中的代码执行完再执行finally后的then链式调用

finally(callback){
     
        return this.then((value)=>{
     
            return MyPromise.resolve(callback(value)).then(value => value)
        },(reason)=>{
     
            return MyPromise.resolve(callback(reason)).then(reason => {
     throw reason})
        })
    }

11.catch

 catch(failCallback){
     
        return this.then(undefined,failCallback);//catch之后还能链式调用then方法多以应该返回一个promise,所以return出去this.then()
    }

完整代码:


/*
 * @Author: liuyj
 * @Date: 2021-01-30 20:07:03
 * @LastEditTime: 2021-01-31 21:38:44
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \my-code\mypromise.js
 */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
     
    constructor(executor) {
     
        try {
     //捕获执行器发生错误
            executor(this.resolve, this.reject)
        } catch (e) {
     
            this.reject(e)
        }
    }

    status = PENDING;//定义初始状态为pending
    value = undefined;//定义成功的值 初始为undefined
    reason = undefined;//定义失败的原因 初始为undefined
    successCallback = [];//保存成功回调函数
    failCallback = [];
    resolve = value => {
     //定义为箭头函数是为了在使用的时候this指向这个类的实例对象 MyPromise
        //如果状态不是pending就阻止向下执行
        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 => {
     
        //如果状态不是pending就阻止向下执行
        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; //判断下then方法中有没有传参数,如果没有默认给一个
        failCallback = failCallback ? failCallback : reason => {
     throw reason;};
        let promise2 = new MyPromise((resolve, reject) => {
     
            if (this.status === FULFILLED) {
     
                setTimeout(() => {
      //这里用setTimeout不是为了延迟而是为了创建异步代码 拿到promise2之后再传递
                    try {
      //捕获回调函数发生错误
                        let x = successCallback(this.value); //这是上一个成功回调函数返回值
                        //去判断返回值是promise还是普通值之后再做相应的操作,
                        // 这里的promise2还拿不到,所以我们这块代码写成异步,等同步代码执行完就会创建完成一个promise2,这时候再去传递
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
     
                        reject(e);//错误传递给下一个then方法的回调
                    }
                }, 0)
            } else if (this.status === REJECTED) {
      //这里的代码几乎和FULFILLED的一样 解决then链式调用中上一个then方法的回调函数有返回值的情况并且能捕获错误
                setTimeout(() => {
      
                    try {
      
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
     
                        reject(e);//错误传递给下一个then方法的回调
                    }
                }, 0)
                
            } else {
     
                //当是等待状态时 不能执行回调函数 要把回调函数保存起来 当异步完成时再执行

                // this.successCallback.push(successCallback);
                // this.failCallback.push(failCallback);
                
                this.successCallback.push(()=>{
     //这里写成这种形式 在循环调用回调函数的时候就不用再传参数了
                    setTimeout(() => {
      //这里的代码几乎和FULFILLED的一样  解决then链式调用中上一个then方法的回调函数有返回值的情况并且能捕获错误
                        try {
      
                            let x = successCallback(this.value); 
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
     
                            reject(e);
                        }
                    }, 0)
                });
                this.failCallback.push(()=>{
     //这里写成这种形式 在循环调用回调函数的时候就不用再传参数了
                    setTimeout(() => {
      //这里的代码几乎和FULFILLED的一样 解决then链式调用中上一个then方法的回调函数有返回值的情况并且能捕获错误
                        try {
      
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
     
                            reject(e);
                        }
                    }, 0)
                });
            }
        })
        return promise2;

    }
    catch(failCallback){
     
        return this.then(undefined,failCallback);//catch之后还能链式调用then方法多以应该返回一个promise,所以return出去this.then()
    }
    finally(callback){
     
        return this.then((value)=>{
     
            return MyPromise.resolve(callback(value)).then(value => value)
        },(reason)=>{
     
            return MyPromise.resolve(callback(reason)).then(reason => {
     throw reason})
        })
    }
    static all(array){
     
        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 是的话将promise返回值放入结果中
                    current.then(
                        (value)=>{
     addData(i,value)},
                        (reason)=>{
     reject(reason)}
                    )
                }else{
     
                    addData(i,current)
                }
            }
        })
    }
    static resolve(value){
     
        if(value instanceof MyPromise) return value;//如果是promise 直接返回promise对象
        // 如果是普通值 要生成一个promise返回
        return new MyPromise(resolve => resolve(value));
    }

}
function resolvePromise(promise2, x, resolve, reject) {
     
    if (promise2 === x) {
     //当then回调函数的返回值是promise对象 并且这个对象是自己本身的时候报错
        return reject(new TypeError('Chaining cycle detected for promise #'))
    }
    if (x instanceof MyPromise) {
      //当then回调函数的返回值是promise对象时
        x.then(resolve, reject);
    } else {
     //当then回调函数的返回值是普通值时
        resolve(x);
    }
}

happy everyday~~~
进阶一Promise源码_第15张图片

你可能感兴趣的:(进阶,学习笔记,前端,js)