乐趣区

关于javascript:手写promise实现着重讲思路

很多大厂在面试的时候都喜爱让面试者手写 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 了,当然这不是残缺的,还有一些中央须要做兼容解决,有想法能够在评论区探讨哦~

退出移动版