如何实现一个Promise

引言

Promise出现解决了js中的回调地狱的问题,使代码更简洁,是ES6中的规范和重要特性。它的使用很简单,但你知道它是怎么样实现的吗~~
如今咱们就来看一下Promise到底是怎么样实现的😄javascript

  • promise规范
    Promise规范是Promise函数中须要遵循的规则, ES6中使用的Promise,它就是遵循Promise/A+规范的。
    既然是有规则可循的,那咱们根据规则来一步步实现Promise

1.建立Promise类

看一下Promise是如何使用的java

const promise = new Promise((resolve, reject) => {
        try {
            resolve('123');
        } catch(err) {
            reject('error');
        }
    });
    
    promise
    .then((msg) => {
        console.log(msg)
    })
复制代码
  • 首先它是一个构造方法,而且接收一个function做为参数,
    而这个function中有resolvereject两个方法。resolve表明成功后返回的值,reject表明拒绝返回的缘由

根据Promise的使用来建立一个叫MyPromise的类git

/** * @params {function} callback 须要执行的业务方法 */
    class MyPromise {
        // 构造方法接收一个function
        constructor(callback) {
            callback(this.resolve, this.reject); // 调用此function
        }
        resolve = (value) => {} // callback中执行的resolve方法
        reject = (reason) => {} // callback中执行的reject方法
    }
    
    // 测试
    var test = new MyPromise((resolve, reject) => {
        console.log('my promise is running!');
    }) // 打印出 my promise is running!
复制代码

2.三种状态

如今咱们建立的类已经能够执行传入的方法了,可是它传入的resolvereject方法是有什么用的呢?
咱们接着看Promise规范 github

  • 根据规范可知Promise有三种状态 pending(等待),fulfilled(完成),rejected(拒绝)。
  • 当状态为pending时,Promise能够变为fulfilledrejected状态
  • 当状态为fulfilled时,Promise不能改变其状态;必须有值且不能改变
  • 当状态为rejected时,Promise不能改变其状态;必须有拒绝的缘由且不能改变

根据Promise规则,接着写刚刚建立的类:面试

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三种状态
    /** * @params {function} callback 须要执行的业务方法 */
    class MyPromise {
        constructor(callback) {
            this.state = stateArr[0]; // 当前状态
            this.value = null; // 完成时的返回值
            this.reason = null; // 失败缘由
            
            callback(this.resolve, this.reject); // 调用此function
        }
        
        // callback中执行的resolve方法
        resolve = (value) => {
            // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新状态为 fulfilled
               this.value = value; // 写入最终的返回值
            }
        }
        
        // callback中执行的reject方法
        reject = (reason) => {
            // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[2]; // 更新状态为 rejected
               this.reason = reason; // 写入拒绝的缘由
            }
        } 
    }
复制代码

测试一下: 数组

能够看到,调用 resolve后,状态变为 fulfilled,再调用 reject时,状态和值都不会改变,这样符合Promise规范~~

3.then方法

咱们的MyPromise写到这里,他已经能够实现更新状态和传值了,可是它的值怎么样输出给咱们的业务呢?
Promise的使用能够看到,它是经过then方法来输出值的。then是是一个必要的方法,看一下then的规范:promise

  • promise必须提供一个then方法去访问他的当前或最终的值或缘由
  • promise中then方法接收两个参数 onFulilledonRejected

下面是关于onFulilledonRejected的规范(部分)异步

  • onFulilledonRejected二者都是一个可选的参数:
    • 若是onFulilled不是一个函数,它必须被忽视
    • 若是onRejected不是一个函数,它必须被忽视
  • 若是onFulilled是一个函数:
    • 它必须在fulfilled时被调用,promise方法中的value做为第一个参数
    • 它必须在fulfilled以前被调用
    • 它不能被屡次调用
  • 若是onRejected是一个函数:
    • 它必须在rejected时被调用,promise方法中的reason做为第一个参数
    • 它必须在rejected以前被调用
    • 它不能被屡次调用
  • 在执行上下文堆栈仅包含平台代码以前,不能调用onFulfilledonRejected
  • onFulfilledonRejected必须是一个函数
  • then能够在同一个promise中屡次被调用
  • then 必须返回一个promise

根据then函数的规则,咱们来设计这个then方法函数

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三种状态
    class MyPromise {
        constructor(callback) {
            this.state = stateArr[0]; // 当前状态
            this.value = null; // 完成时的返回值
            this.reason = null; // 失败缘由
            
            callback(this.resolve, this.reject); // 调用此function
        }
        
        // callback中执行的resolve方法
        resolve = (value) => {
            // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新状态为 fulfilled
               this.value = value; // 写入最终的返回值
            }
        }
        
        // callback中执行的reject方法
        reject = (reason) => {
            // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[2]; // 更新状态为 rejected
               this.reason = reason; // 写入拒绝的缘由
            }
        }
        
        // then方法
        then = (onFulilled, onRejected) => {
            // 判断onFulilled 和 onRejected是不是一个函数,若是不是函数则忽略它
            onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) => value;
            onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;
            
            // 若是状态是fulfilled
            if (this.state === stateArr[1]) {
                // then返回的必须是一个promise
                return new MyPromise((resolve, reject) => {
                    try {
                        const result = onFulilled(this.value); // 执行传入的onFulilled方法
                        
                        // 若是onFulilled返回的是一个Promise,则调用then方法
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
            }
            
            // 若是状态是rejected
            if (this.state === stateArr[2]) {
                // then返回的必须是一个promise
                return new MyPromise((resolve, reject) => {
                    try {
                        const result = onRejected(this.reason); // 执行传入的onRejected方法
                        
                        // 若是onRejected返回的是一个Promise,则调用then方法
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
            }
        }
    }

复制代码

测试一下:oop

成功返回:

失败返回:

4.继续完善

至此,咱们的MyPromise的已经基本能够运行了,可是如今有一个很严重的缺陷,若是遇到异步的请求时候,resolve不能按上下文执行,这会致使then方法执行失败例如

var test = new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(123);
        }, 2000)
    })
    .then(msg => {
        console.log(msg);
        return 456;
    })
复制代码

由于在调用then方法的时候,promise的状态尚未改变,而咱们的then方法尚未处理pending状态的逻辑。 这致使执行异步方法的时候,then方法不会返回任何东西
好比,在上面的例子中,javscript已经把then方法执行了,但setTimeout中的方法还在eventloop中等待执行。 这样须要作的是:

  • then中的方法保存起来,等待resolvereject执行后再调用刚刚保存的then中的方法
  • 因为在这期间可能会有多个then方法会被执行,因此须要用一个数据来保存这些方法
    根据这两点,再来修改一些代码
// 在constructor中新增两个数组分别用于装then中的resolve和reject方法
    constructor(callback) {
        this.resolveArr = [];
        this.rejectArr = [];
    }
    
    // 修改resolve方法
    resolve = (value) => {
        // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新状态为 fulfilled
               this.value = value; // 写入最终的返回值
               
               this.resolveArr.forEach(fun => fun(value)) // 循环执行then已插入的resolve方法
            }
    }
    
    // 修改reject方法
    reject = (reason) => {
        // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新状态为 fulfilled
               this.reason = reason; // 写入最终的返回值
               
               this.rejectArr.forEach(fun => fun(reason)) // 循环执行then已插入的reject方法
            }
    }
    
    // then方法中须要添加捕捉pending状态的逻辑
    then = (onFulilled, onRejected) => {
        // 若是状态为pending
        if (this.state === stateArr[0]) {
            return new Promise((resolve, reject) => {
                // 插入成功时调用的函数
                this.resolveArr.push((value) => {
                    try {
                        const result = onFulilled(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
                
                // 插入失败时调用的函数
                this.rejectArr.push((value) => {
                    try {
                        const result = onRejected(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err)
                    }
                })
            })
        }
    }
复制代码

写好了,测试一下~

new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(123);
        }, 2000)
    })
    .then(msg => {
        console.log(msg);
        return new MyPromise((resolve, reject) => {
			setTimeout(()=> {
				resolve(456)
			}, 2000);
		})
    })
    .then(msg => {
        console.log(msg);
    })
复制代码

5.Promise的其余方法

根据Promise规范实现的Promise大体已经完成啦,最后咱们把Promise中实现的方法也补一下

  • catch方法
// 在MyPromise原型中实现
    class MyPromise {
        // 调用then中的reject
        catch = (reject) => {
            this.then(null, reject);
        }
    }
复制代码
  • resolve
MyPromise.resolve = (value) => {
        return new MyPromise((resolve, reject) => { resolve(value) });
    }
复制代码
  • reject
MyPromise.resolve = (reason) => {
        return new MyPromise((resolve, reject) => { reject(reason) });
    }
复制代码
  • 还有allracefinally(原型方法),其实都是根据Promise中的原型方法和Promise规则实现的,这里就不一一列举啦。须要了解的小伙伴能够自行去看

最终代码(源码地址)

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三种状态
class MyPromise {
    constructor(callback) {
        this.state = stateArr[0]; // 当前状态
        this.value = null; // 完成时的返回值
        this.reason = null; // 失败缘由
        this.resolveArr = [];
        this.rejectArr = [];
        
        callback(this.resolve, this.reject); // 调用此function
    }
    
    // callback中执行的resolve方法
    resolve = (value) => {
        // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
                this.state = stateArr[1]; // 更新状态为 fulfilled
                this.value = value; // 写入最终的返回值
               
                this.resolveArr.forEach(fun => fun(value)) // 循环执行then已插入的resolve方法
            }
    }
    
    // callback中执行的reject方法
    reject = (reason) => {
        // 判断状态是否须要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新状态为 fulfilled
               this.reason = reason; // 写入最终的返回值
               
               this.rejectArr.forEach(fun => fun(reason)) // 循环执行then已插入的reject方法
            }
    }
    
    // then方法
    then = (onFulilled, onRejected) => {
        // 判断onFulilled 和 onRejected是不是一个函数,若是不是函数则忽略它
        onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) => value;
        onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;

        // 若是状态为pending
        if (this.state === stateArr[0]) {
            return new MyPromise((resolve, reject) => {
                // 插入成功时调用的函数
                this.resolveArr.push((value) => {
                    try {
                        const result = onFulilled(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
                
                // 插入失败时调用的函数
                this.rejectArr.push((value) => {
                    try {
                        const result = onRejected(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err)
                    }
                })
            })
            
        }
        
        // 若是状态是fulfilled
        if (this.state === stateArr[1]) {
            // then返回的必须是一个promise
            return new MyPromise((resolve, reject) => {
                try {
                    const result = onFulilled(this.value); // 执行传入的onFulilled方法
                    
                    // 若是onFulilled返回的是一个Promise,则调用then方法
                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch(err) {
                    reject(err);
                }
            })
        }
        
        // 若是状态是rejected
        if (this.state === stateArr[2]) {
            // then返回的必须是一个promise
            return new MyPromise((resolve, reject) => {
                try {
                    const result = onRejected(this.reason); // 执行传入的onRejected方法
                    
                    // 若是onRejected返回的是一个Promise,则调用then方法
                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch(err) {
                    reject(err);
                }
            })
        }
    }

    // 调用then中的reject
    catch = (reject) => {
        this.then(null, reject);
    }
}

MyPromise.resolve = (value) => {
    return new MyPromise((resolve, reject) => { resolve(value) });
}

MyPromise.resolve = (reason) => {
    return new MyPromise((resolve, reject) => { reject(reason) });
}

复制代码

小结

此次咱们了解了promise是如何实现的:

  • 必须是构造函数
  • 三种状态(pending,resolve,reject)
  • then方法(promise中必需要有的方法)

从构造函数开始,到三种状态的实现,最后实现then方法一步步根据Promise规则来实现Promise。了解完之后就能够在面试官面前手写一个Promise啦!😄

相关文章
相关标签/搜索