很多大厂在面试的时候都喜欢让面试者手写promise,所以有时候我们也要面向面试编程。

要想实现自己的promise,首先我们看看官方promise的用法,请看下面这段代码:

const foo = () => {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
          resolve('111')
      }, 1000);
  })
}
foo().then((res) => {
  console.log(res);
})
console.log('hello');

// 先输出 'hello' 再输出 '111'

首先,我们根据这段代码,来梳理下思路:

  1. 先声明一个class类,我们叫它 MyPromise;
  2. MyPromise类接受一个函数fn作为参数,且fn有两个参数resolvereject,均为函数;
  3. 定义MyPromise类的三种状态,pendingfulfilledrejected;
  4. MyPromise类的constructor里要执行函数fn, 把resolvereject传递给fn,执行resolvereject时要改变MyPromise类的状态;
  5. 实现then方法,then方法接收两个函数作为参数;
  6. 实现链式调用;

接下来让我们用代码实现。

第一步:

// 定义MyPromise的三种状态
        const PENDING = 'pending';
        const FULFILLED = 'fulfilled';
        const REJECTED = 'rejected';

        class MyPromise {
            // 类的属性可以直接定义在顶层
            status = PENDING; // 初始状态设为 pending
            result = undefined; // resolve传递的结果
            reason = undefined; // reject传递的结果

            constructor(fn) {
                // 定义resolve函数
                const resolve = (result) => {
                    // 只有是pending状态才能修改为fulfilled状态
                    if (this.status === PENDING) {
                        // 把结果存起来,同时改变状态
                        this.result = result;
                        this.status = FULFILLED;
                    }
                }
                // 定义reject函数
                const reject = (reason) => {
                    // 只有是pending状态才能修改为rejected状态
                    if (this.status === PENDING) {
                        // 把结果存起来,同时改变状态
                        this.reason = reason;
                        this.status = REJECTED;
                    }
                }
                // new的时候接收的函数,立即执行
                fn(resolve, reject);
            }
        }

解释:其实就是定义了一个类,然后定义了构造函数,构造函数接受一个函数,并立即执行,其实这也是 new 操作的基本过程;

第二步:

// 定义MyPromise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    // 类的属性可以直接定义在顶层
    status = PENDING; // 初始状态设为 pending
    result = undefined; // resolve传递的值
    reason = undefined; // reject传递的值

    constructor(fn) {
        // 定义resolve函数
        const resolve = (result) => {
            // 只有是pending状态才能修改为fulfilled状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.result = result;
                this.status = FULFILLED;
            }
        }
        // 定义reject函数
        const reject = (reason) => {
            // 只有是pending状态才能修改为rejected状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.reason = reason;
                this.status = REJECTED;
            }
        }
        // new的时候接收的函数,立即执行
        fn(resolve, reject);
    }
    
    // 定义then方法 接收两个函数作为参数
    then(onResolve, onReject) {
        // 如果状态为 fulfilled , 执行onResolve, 并把执行resolve函数时存的结果传入
        if(this.status === FULFILLED) {
            onResolve(this.result);
        }
        // 如果状态为 rejected , 执行onReject, 并把执行reject函数时存的结果传入
        if(this.status === REJECTED) {
            onReject(this.reason)
        }
    }
}

解释:相比第一步,增加了then方法,then方法其实很简单,接受两个函数并执行,这两个函数的参数是类的resultreason属性值,其实也就是执行resolvereject函数时,传入的值;

这时候MyPromise的基本功能已经实现(开心😊),让我们来试用一下:

const foo = () => {
  return new MyPromise((resolve, reject) => {
      setTimeout(() => {
          resolve('111')
      }, 1000);
  })
}
foo().then((res) => {
  console.log(res);
})
console.log('hello');

// 输出 'hello'

等等,貌似有点不对!怎么只输出了‘hello’而没有输出‘111’呢?😱😱😱

遇到事情不要慌,我们来捋一下逻辑:

从最近的代码片段可以看出,resolve函数是在setTimeout里延时了1秒钟执行的,而then方法是同步执行的,是在resolve执行之前就执行了,这时候MyPromise的状态还是'pending',所以根本就没有执行onResolve方法,自然就没有打印'111';

找到了原因所在,接下来我们就改造下then方法:

// 定义MyPromise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    // 类的属性可以直接定义在顶层
    status = PENDING; // 初始状态设为 pending
    result = undefined; // resolve传递的值
    reason = undefined; // reject传递的值
    onResolveArr = []; // 存入then方法的第一个参数onResolve,等待状态变为fulfilled的时候再去执行;
    onRejectArr = []; // 存入then方法的第二个参数onReject,等待状态变为rejected的时候再去执行;

    constructor(fn) {
        // 定义resolve函数
        const resolve = (result) => {
            // 只有是pending状态才能修改为fulfilled状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.result = result;
                this.status = FULFILLED;
                // 执行resolve时,把所有存入onResolveArr的函数都执行一遍
                this.onResolveArr.map(onResolve => onResolve());
            }
        }
        // 定义reject函数
        const reject = (reason) => {
            // 只有是pending状态才能修改为rejected状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.reason = reason;
                this.status = REJECTED;
                // 执行reject时,把所有存入onRejectArr的函数都执行一遍
                this.onRejectArr.map(onReject => onReject());
            }
        }
        // new的时候接收的函数,立即执行
        fn(resolve, reject);
    }
    
    // 定义then方法 接收两个函数作为参数
    then(onResolve, onReject) {
        // 如果状态为pending, 暂时先把onResolve、onReject存入对应的数组里
        // 为什么要存到数组里?因为new MyPromise()可能会调用多个then方法(注意这里不是链式调用)
        if(this.status === PENDING) {
            // 因为onResolve、onReject要接受参数,所以这里要push一个匿名函数;
            this.onResolveArr.push(() => {
                onResolve(this.result);
            })
            this.onRejectArr.push(() => {
                onReject(this.reason);
            })
        }
        // 如果状态为 fulfilled , 执行onResolve, 并把执行resolve函数时存的结果传入
        if(this.status === FULFILLED) {
            onResolve(this.result);
        }
        // 如果状态为 rejected , 执行onReject, 并把执行reject函数时存的结果传入
        if(this.status === REJECTED) {
            onReject(this.reason)
        }
    }
}

解释:可以看到,在then方法里新增了一个判断,当状态为pending的时候,把onResolve、onReject存入对应的数组中,以便在将来执行resolvereject的时候去执行,类似于设计模式中的发布、订阅模式

这个时候,then方法基本就实现了,再执行刚才那个例子,发现可以正常的返回'hello''111'了。

But!现在还不能链式调用😂😂😂,因为现在then方法并没有返回值,所以在执行第二个then方法时会报错Cannot read properties of undefined,接下来我们继续实现链式调用:

// 定义MyPromise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    // 类的属性可以直接定义在顶层
    status = PENDING; // 初始状态设为 pending
    result = undefined; // resolve传递的值
    reason = undefined; // reject传递的值
    onResolveArr = []; // 存入then方法的第一个参数onResolve,等待状态变为fulfilled的时候再去执行;
    onRejectArr = []; // 存入then方法的第二个参数onReject,等待状态变为rejected的时候再去执行;

    constructor(fn) {
        // 定义resolve函数
        const resolve = (result) => {
            // 只有是pending状态才能修改为fulfilled状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.result = result;
                this.status = FULFILLED;
                // 执行resolve时,把所有存入onResolveArr的函数都执行一遍
                this.onResolveArr.map(onResolve => onResolve());
            }
        }
        // 定义reject函数
        const reject = (reason) => {
            // 只有是pending状态才能修改为rejected状态
            if (this.status === PENDING) {
                // 把结果存起来,同时改变状态
                this.reason = reason;
                this.status = REJECTED;
                // 执行reject时,把所有存入onRejectArr的函数都执行一遍
                this.onRejectArr.map(onReject => onReject());
            }
        }
        // new的时候接收的函数,立即执行
        fn(resolve, reject);
    }

    // 处理何时执行第二个promise的resolve、reject函数
    handleNewPromise(res, newPromise, resolve, reject) {
        // 如果第一个then函数返回的值是当前promise,则会发生循环引用
        if (res === newPromise) {
            return reject(new TypeError('Chaining cycle detected for promise'))
        }
        // 防止多次调用
        let called;
        // 如果是对象类型或函数类型
        if (typeof res === 'object' || typeof res === 'function') {
            // 获取第一个then方法的返回值的then属性,如果是个函数,则默认为promise
            const then = res.then;
            // 如果res是promise,则直接执行then方法
            if (typeof then === 'function') {
                // 传入this、新的onResolve、新的onReject
                then.call(res, newRes => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    // 递归调用handleNewPromise  直到res不是object或function
                    this.handleNewPromise(newRes, newPromise, resolve, reject);
                }, newReason => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    reject(newReason);
                })
            } else {
                resolve(res);
            }
        } else {
            // 如果是值类型  直接执行第二个promise的resolve方法  并把结果返回
            resolve(res);
        }
    }

    // 定义then方法 接收两个函数作为参数
    then(onResolve, onReject) {
        // then方法返回一个新的promise,以便于链式调用
        const newPromise = new MyPromise((resolve, reject) => {
            // 如果状态为pending, 暂时先把onResolve、onReject存入对应的数组里
            // 为什么要存到数组里?因为new MyPromise()可能会调用多个then方法(注意这里不是链式调用)
            if (this.status === PENDING) {
                // 因为onResolve、onReject要接受参数,所以这里要push一个匿名函数;
                this.onResolveArr.push(() => {
                    const result = onResolve(this.result);
                    this.handleNewPromise(result, newPromise, resolve, reject);
                })
                this.onRejectArr.push(() => {
                    const reason = onReject(this.reason);
                    this.handleNewPromise(reason, newPromise, resolve, reject);
                })
            }
            // 如果状态为 fulfilled , 执行onResolve, 并把执行resolve函数时存的结果传入
            if (this.status === FULFILLED) {
                const result = onResolve(this.result);
                this.handleNewPromise(result, newPromise, resolve, reject);
            }
            // 如果状态为 rejected , 执行onReject, 并把执行reject函数时存的结果传入
            if (this.status === REJECTED) {
                const reason = onReject(this.reason);
                this.handleNewPromise(reason, newPromise, resolve, reject);
            }
        })
        return newPromise;
    }
}

解释:then方法返回了新的promise, 新增了handleNewPromise方法,主要是判断第一个then函数的返回结果,然后根据不同类型,做相应的具体操作,其实主要是判断第一个then的返回结果是不是promise,如果是的话,递归调用handleNewPromise方法。

到这里,已经基本实现自己的promise了,当然这不是完整的,还有一些地方需要做兼容处理,有想法可以在评论区讨论哦~


huangsh
281 声望16 粉丝

灵魂和皮囊能够握手言和,面子和里子能够始终如一。