根据Promise/A+ 规范简单实现一个Promise


theme: smartblue

根据Promise/A+ 规范简单实现一个Promise

最近在复习准备面试,重温了一下Promise实现,简单说说我自己的一个实现思路

1. Promise/A+ 规范

这里只列举一些比较核心的

  • promise状态必须是这三个状态中的一种:等待态pending,解决态fulfilled或拒绝态rejected

  • promise状态是不可逆的,状态的变更是单向的,只能从Pending -> Fulfilled 或 Pending -> Rejected

  • Promise必须提供一个then方法来访问当前或最终的值或原因。

    Promisethen方法接受俩个参数:

    promise.then(onFulfilled, onRejected)
    
  • 如果onFulfilled不是一个函数,它必须被忽略,如果onRejected不是一个函数,它必须被忽略
  • then方法返回一个promise,then 方法可以被同一个 promise 调用多次。

2. 原生Promise使用思路

//创建一个Promise
let promise = new Promise((resolve, reject)=>{
    resolve('result');
});
​
promise.then(
    result => console.log(result),
    err => console.log(err)
);

根据上面代码,我们分析一下:

  • Promise是new出来的,说明它是一个构造函数
  • Promise可接收一个函数,函数在new Promise()时立即执行
  • 函数参数为resolvereject,代表成功/失败方法
  • then()方法可传入收集成功/失败回调,并且根据Promise/A+ 规范,它可返回一个Promise,可链式调用

3. 根据思路实现Promise

根据上面思路,我们一步步去进行实现

1. 使用class实现,实现传入方法并执行
//实现Promise
class NewPromise {
    constructor(fn) {
        this.result = null;     //定义result存储传入值
        //传入方法直接执行
        fn(this.resolve, this.reject);
    }
    //定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
    resolve = (result) => {
        this.result = result;   //接收传入值
    }
    reject = (result) => {
        this.result = result;
    }
}
2. 根据Promise/A+ 规范,定义状态值
//实现Promise
class NewPromise {
    //定义Promise状态值
    static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
    constructor(fn) {
        this.result = null;                 //定义result存储传入值
        this.status = NewPromise.PENDING;   //定义初始化状态,初始化状态为待定
        //传入方法直接执行
        fn(this.resolve, this.reject);
    }
    //定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
    resolve = (result) => {
        //状态是单向且不可逆的,所以如果判断不为待定态则return
        if (this.status !== NewPromise.PENDING) return;
        this.status = NewPromise.FULFILLED;     //改变状态为对应状态
        this.result = result;                   //接收传入值
    }
    reject = (result) => {
        if (this.status !== NewPromise.PENDING) return;
        this.status = NewPromise.REJECTED;
        this.result = result;
    }
}
3. 实现then方法
//实现Promise
class NewPromise {
    ...then = (onResolve, onReject) => {
        //根据状态使用对应方法
        switch (this.status) {
            case NewPromise.FULFILLED:
                onResolve(this.result);
                break;
            case NewPromise.REJECTED:
                onReject(this.result);
                break;
        }
    }
}

这时候我们测试一下代码

//简单测试一下
let promise = new NewPromise((resolve, reject) => {
    resolve('result');
});
​
promise.then(
    result => console.log(result),
    err => console.log(err)
);                      //输出了result,结果比较正常//但是我们换种写法
let promise = new NewPromise((resolve, reject) => {
    console.log('第一步');
    resolve('第二步');
});
​
promise.then(
    result => console.log(result),
    err => console.log(err)
);
​
console.log('第三步');      //输出结果为  第一步   第二步   第三步//我们测试下原生Promise的
let promise = new Promise((resolve, reject) => {
    console.log('第一步');
    resolve('第二步');
});
​
promise.then(
    result => console.log(result),
    err => console.log(err)
);
​
console.log('第三步');      //输出结果为  第一步   第三步   第二步

从测试发现,我们的输出结果与原生Promise有所不同,问题很简单,then是微任务,需要异步执行,我们二话不说,直接给then里面回调方法加上setTimeout

//实现Promise
class NewPromise {
    ...
    
    then = (onResolve, onReject) => {
        //根据状态使用对应方法
        switch (this.status) {
            case NewPromise.FULFILLED:
                setTimeout(() => {
                    onResolve(this.result);
                });
                break;
            case NewPromise.REJECTED:
                setTimeout(() => {
                    onReject(this.result);
                });
                break;
        }
    }
}

这时候的测试结果就正常了,但是问题其实还没有解决,我们再举个例子,在resolve外层包裹个setTimeout,使得resolve异步执行

let promise = new NewPromise((resolve, reject) => {
    console.log('第一步');
    setTimeout(() => {
        resolve('第二步');
    });
});
​
promise.then(
    result => console.log(result),
    err => console.log(err)
);
​
console.log('第三步');      //输出结果为  第一步   第三步,缺少了第二步

代码执行到then的时候出现了问题,并没有输出第二步,我们调试代码就会发现,当resolve变成异步时,执行到thenPromise为PENDING状态,而我们then方法里面并没有对于PENDING状态的处理,所以我们需要在then里面去添加一个PENDING状态处理,但是我们要考虑以下几点:

  • then在PENDING状态下,因为resolvereject都没有触发过,所以没有获取到result
  • then里面的onResolveonReject存储起来,待resolvereject执行后再执行

考虑完这几个点后,我们进行实现

//实现Promise
class NewPromise {
    //定义Promise状态值
    static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
    constructor(fn) {
        this.result = null;                 //定义result存储传入值
        this.status = NewPromise.PENDING;   //定义初始化状态,初始化状态为待定
        //创建成功/失败数组存储成功/失败回调
        this.resolveCallback = [];
        this.rejectCallback = [];
        //传入方法直接执行
        fn(this.resolve, this.reject);
    }
    //定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
    resolve = (result) => {
        //添加定时器,确保resolve是在事件循环末尾执行
        setTimeout(() => {
            //状态是单向且不可逆的,所以如果判断不为待定态则return
            if (this.status !== NewPromise.PENDING) return;
            this.status = NewPromise.FULFILLED;     //改变状态为对应状态
            this.result = result;                   //接收传入值
            this.resolveCallback.forEach(callback => {
                callback(result);
            })
        });
    }
    reject = (result) => {
        setTimeout(() => {
            if (this.status !== NewPromise.PENDING) return;
            this.status = NewPromise.REJECTED;
            this.result = result;
            this.rejectCallback.forEach(callback => {
                callback(result);
            })
        });
    }then = (onResolve, onReject) => {
        //根据状态使用对应方法
        switch (this.status) {
            case NewPromise.PENDING:    //添加待定状态处理,存储成功失败回调
                this.resolveCallback.push(onResolve);
                this.rejectCallback.push(onReject);
                break;
            case NewPromise.FULFILLED:
                setTimeout(() => {
                    onResolve(this.result);
                });
                break;
            case NewPromise.REJECTED:
                setTimeout(() => {
                    onReject(this.result);
                });
                break;
        }
    }
}
4. .then()链式调用

根据Promise/A+ 规范,.then()需要返回一个Promise,并且.then()需要拿到上一个.then()的返回值,我们按照这个思路简单实现一下

//实现Promise
class NewPromise {
    ...then = (onResolve, onReject) => {
        return new NewPromise((resolve, reject) => {        //返回一个Promise
            // 简单处理下成功/失败回调
            const resolveFn = res => {
                const x = onResolve(res);   //执行当前Promise的成功回调,获取返回值
                resolve(x);                 //执行resolve,下一个then就能获取到x值
            }
            const rejectFn = err => {
                const x = onReject(err);    //执行当前Promise的成功回调,获取返回值
                resolve(x);                 //执行resolve,下一个then就能获取到x值
            }
            //根据状态使用对应方法
            switch (this.status) {
                case NewPromise.PENDING:    //添加待定状态处理,存储成功失败回调
                    this.resolveCallback.push(resolveFn);
                    this.rejectCallback.push(rejectFn);
                    break;
                case NewPromise.FULFILLED:
                    setTimeout(() => {
                        onResolve(this.result);
                    });
                    break;
                case NewPromise.REJECTED:
                    setTimeout(() => {
                        onReject(this.result);
                    });
                    break;
            }
        })
    }
}

上面我们简单实现了链式调用,但是考虑得还不够前面,成功/失败的x值不为Promise对象时,才能直接使用用resolve,如果x的值为应该Promise对象,我们需要判断它的成功/失败状态去调用对应的方法

//实现Promise
class NewPromise {
    ...then = (onResolve, onReject) => {
        return new NewPromise((resolve, reject) => {        //返回一个Promise
            // 简单处理下成功/失败回调,同时继续报错处理
            const resolveFn = res => {
                try {
                    const x = onResolve(res);   //执行当前Promise的成功回调,获取返回值
                    //这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            const rejectFn = err => {
                try {
                    const x = onReject(err);
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            //根据状态使用对应方法
            switch (this.status) {
                case NewPromise.PENDING:    //添加待定状态处理,存储成功失败回调
                    this.resolveCallback.push(resolveFn);
                    this.rejectCallback.push(rejectFn);
                    break;
                case NewPromise.FULFILLED:
                    setTimeout(() => {
                        onResolve(this.result);
                    });
                    break;
                case NewPromise.REJECTED:
                    setTimeout(() => {
                        onReject(this.result);
                    });
                    break;
            }
        })
    }
}
5. 值穿透处理

根据规范,如果 .then() 接收的参数不是function,那么我们应该忽略它。如果没有忽略,当.then()回调不为function时将会抛出异常,导致链式调用中断

//实现Promise
class NewPromise {
    ...then = (onResolve, onReject) => {
        // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
        typeof onResolve !== 'function' ? onResolve = value => value : null;
        typeof onReject !== 'function' ? onReject = reason => {
            throw new Error(reason instanceof Error ? reason.message : reason);
        } : null;return new NewPromise((resolve, reject) => {        //返回一个Promise
            // 简单处理下成功/失败回调,同时继续报错处理
            const resolveFn = res => {
                try {
                    const x = onResolve(res);   //执行当前Promise的成功回调,获取返回值
                    //这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            const rejectFn = err => {
                try {
                    const x = onReject(err); 
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            //根据状态使用对应方法
            switch (this.status) {
                case NewPromise.PENDING:    //添加待定状态处理,存储成功失败回调
                    this.resolveCallback.push(resolveFn);
                    this.rejectCallback.push(rejectFn);
                    break;
                case NewPromise.FULFILLED:
                    setTimeout(() => {
                        onResolve(this.result);
                    });
                    break;
                case NewPromise.REJECTED:
                    setTimeout(() => {
                        onReject(this.result);
                    });
                    break;
            }
        })
    }
}

完整代码

//实现Promise
class NewPromise {
    //定义Promise状态值
    static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
    constructor(fn) {
        this.result = null;                 //定义result存储传入值
        this.status = NewPromise.PENDING;   //定义初始化状态,初始化状态为待定
        //创建成功/失败数组存储成功/失败回调
        this.resolveCallback = [];
        this.rejectCallback = [];
        //传入方法直接执行
        fn(this.resolve, this.reject);
    }
    //定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
    resolve = (result) => {
        //添加定时器,确保resolve是在事件循环末尾执行
        setTimeout(() => {
            //状态是单向且不可逆的,所以如果判断不为待定态则return
            if (this.status !== NewPromise.PENDING) return;
            this.status = NewPromise.FULFILLED;     //改变状态为对应状态
            this.result = result;                   //接收传入值
            this.resolveCallback.forEach(callback => {
                callback(result);
            })
        });
    }
    reject = (result) => {
        setTimeout(() => {
            if (this.status !== NewPromise.PENDING) return;
            this.status = NewPromise.REJECTED;
            this.result = result;
            this.rejectCallback.forEach(callback => {
                callback(result);
            })
        });
    }then = (onResolve, onReject) => {
        // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
        typeof onResolve !== 'function' ? onResolve = value => value : null;
        typeof onReject !== 'function' ? onReject = reason => {
            throw new Error(reason instanceof Error ? reason.message : reason);
        } : null;return new NewPromise((resolve, reject) => {        //返回一个Promise
            // 简单处理下成功/失败回调,同时继续报错处理
            const resolveFn = res => {
                try {
                    const x = onResolve(res);   //执行当前Promise的成功回调,获取返回值
                    //这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            const rejectFn = err => {
                try {
                    const x = onReject(err);
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            //根据状态使用对应方法
            switch (this.status) {
                case NewPromise.PENDING:    //添加待定状态处理,存储成功失败回调
                    this.resolveCallback.push(resolveFn);
                    this.rejectCallback.push(rejectFn);
                    break;
                case NewPromise.FULFILLED:
                    setTimeout(() => {
                        onResolve(this.result);
                    });
                    break;
                case NewPromise.REJECTED:
                    setTimeout(() => {
                        onReject(this.result);
                    });
                    break;
            }
        })
    }
}

上面只是实现了Promise的一个基本功能,要扩展别的功能其实知道原理的话,很容易实现,比如.catch()

//实现Promise
class NewPromise {
    ...catch = (reject) => {
        return this.then(undefined, reject);
    }
}

把原理弄懂弄透的话,手写出Promise其实很简单

推荐文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值