最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 『面试的底气』—— 实现一个Promise

    正文概述 掘金(红尘炼心)   2020-11-26   465

    前言

    异步行为是 JavaScript 的基础,但以前的实现不理想。在早期的 JavaScript 中,只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(俗称 “地狱回调” )来解决。

    为了解决地狱回调这个问题,ES6 根据 Promises/A+ 规范,实现了一个 Promise ,可以将异步行为以同步行为的流程表达出来。

    ES6 从2015发布到现在,其中 Promise 已经在开发中到处使用,或许在3年前的中高级前端面试中会问 Promise 的概念和用法,现在一般都会叫你实现一个 Promise,这样不仅考察了你对 Promise 的概念和用法的掌握程度,也考察了你对 ES6 中语法及其应用的掌握程度。

    想要实现一个 Promise,就要遵循 Promise/A+ 规范,业界所有 Promise 的类库都遵循这个规范。当然规范也是根据业务场景一点点完善的,本专栏从业务场景入手,带你一步一步实现一个 Promise ,比较规范不好记忆,业务场景做多了还不好记忆吗。

    希望通过本专栏给你在中高级前端的面试中增加点底气。

    一、分析 Promise 的业务场景

    先来简单分析一下 Promise 的业务场景。

    • Promise 中有三个状态 Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。外界无法改变这个三个状态,而且一旦状态改变就不会再变。
    • 实例化一个 Promise 需要传入一个 executor 函数 ,业务代码在 executor 函数中执行,另外 executor 函数接收两个参数 resolverejectresolvereject 是 Promise 构造函数的内置函数。

      new Promise((resolve, reject) =>{
          //... 业务代码
      })
      
    • executor 函数中业务代码执行成功了,调用 resolve 函数,把 Promise 的状态变为已成功,另外通过参数把业务代码的执行成功的结果传递到 Promise 中。

    • executor 函数中业务代码执行失败了,调用 resolve 函数,把 Promise 的状态变为已失败,另外通过参数把业务代码的执行失败的原因传递到 Promise 中。

    • 实例方法 then 的第一个参数是业务代码执行成功的回调函数,第二个参数是业务代码执行失败的回调函数,当业务代码执行完毕后,会根据执行结果调用对应的回调函数,且这些回调函数接收业务代码的执行结果作为参数。

    • 通过实例方法 catch 来添加业务代码执行失败的回调函数。

    下面就一一来实现 Promise 的功能。

    二、初步搭建 Promise 的构造函数

    按照上面对 Promise 的业务场景分析,Promise 的构造函数应该有这些内容:

    • Promise 是个类,在 ES6 中用 Class 语法创建。

    • Promise 构造函数接收 executor 函数作为参数,且在其中执行 executor 函数。

    • Promise 构造函数中有 resolvereject 内置方法,并作为参数传递给 executor 函数。

    • 设置个实例属性 status 来存储状态。

    • 内置函数 resolve 可以把状态变为已成功,内置函数 reject 可以把状态变为已失败,且一旦状态改变就不会再变。

    另外在构造器外面用 Symbol 来创建三个状态 Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。

    // 用Symbol定义三种状态,防止外界改变状态。
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    class Promise {
        constructor(executor){
            this.status = Pending;//存储 Promise 的状态
            const resolve = () =>{
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if(this.status === Pending){
                    this.status = Fulfilled;
                }
            };
            const reject = () =>{
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if(this.status === Pending){
                    this.status = Rejected;
                }
            };
            executor(resolve,reject);
        },
    }
    

    三、初步实现 then 实例方法

    按照上面对 then 实例方法的业务场景的简单分析,在 then 实例方法中调用回调函数时,还要把 executor 函数中业务代码的执行结果作为参数传递进去,那么要新增实例属性来存储业务代码的执行结果。另外执行成功的结果通过内置方法 resolve 的参数传入,其执行失败的原因通过内置方法 reject 的参数传入。

    // 在这里用Symbol定义三种状态,防止外部改变状态
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    class Promise {
        constructor(executor) {
            this.status = Pending;//存储 Promise 的状态
            this.value = undefined;//存储executor函数中业务代码执行成功的结果
            this.reason = undefined;//存储executor函数中业务代码执行失败的原因
            const resolve = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Fulfilled;
                    this.value = value;
                }
            };
            const reject = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Rejected;
                    this.reason = value;
                }
            };
            executor(resolve, reject);
        }
        then(onFulfilled, onRejected) {
        	if(this.status === Fulfilled){
                if (onFulfilled && typeof onFulfilled === 'function') {
                	onFulfilled(this.value)
                }
            }
            if(this.status === Rejected){
                if (onRejected && typeof onRejected === 'function') {
                	onRejected(this.reason)
                }
            }
        }
    }   
    

    用个测试用例验证一下:

    const test = new Promise((resolve,reject) =>{
        resolve('"执行成功"')
    })
    test.then(res =>{
        console.log(res)
    })
    

    在控制台上可以打印出 "执行成功",但是这里只处理了同步操作的 promise。如果在 executor 函数中传入一个异步操作的话呢?

    const test = new Promise((resolve,reject) =>{
          setTimeout(() =>{
              resolve('"执行成功"')
          },1000)
    })
    test.then(res =>{
        console.log(res)
    })
    

    1 秒后,会发现控制台上并没有打印出 "执行成功",因为调用 then 实例方法时,Promise 的状态是 Pending ,虽然1秒后 Promise 的状态变为 Fulfilled ,但是 then 实例方法已经调用过了。

    那么要怎么控制 then 实例方法中回调函数的执行时机。可以用发布者——订阅者的设计模式来实现。

    当调用 then 实例方法时,如果 Promise 的状态是 Pending 时,先将成功回调函数和失败回调函数分别存放起来,在 executor 函数中异步任务执行结束,触发内置方法 resolvereject,在其中去依次调用这些回调函数。

    依据这个思路,再改一下代码。

    // 在这里用Symbol定义三种状态,防止外部改变状态
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    class Promise {
        constructor(executor) {
            this.status = Pending;//存储 Promise 的状态
            this.value = undefined;//存储executor函数中业务代码执行成功的结果
            this.reason = undefined;//存储executor函数中业务代码执行失败的原因
            this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
            this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
            const resolve = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Fulfilled;
                    this.value = value;
                    // 依次调用成功回调函数
                    this.onFulfilled.forEach(fn=>fn());
                }
            };
            const reject = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Rejected;
                    this.reason = value;
                    // 依次调用失败回调函数
                    this.onRejected.forEach(fn=>fn());
                }
            };
            executor(resolve, reject);
        }
        then(onFulfilled, onRejected) {
        	if(this.status === Fulfilled){
                if (onFulfilled && typeof onFulfilled === 'function') {
                	onFulfilled(this.value)
                }
            }
            if(this.status === Rejected){
                if (onRejected && typeof onRejected === 'function') {
                	onRejected(this.reason)
                }
            }
            if(this.status === Pending){
                if (onFulfilled && typeof onFulfilled === 'function') {
                    this.onFulfilled.push(() =>{
                         onFulfilled(this.value)
                    })
                }
                if (onRejected && typeof onRejected === 'function') {
                	this.onRejected.push(() =>{
                        onRejected(this.reason)
                    })
                }
            }
        }
    }   
    

    在用上面的测试用例测试一下,1 秒之后控制台打印出 "执行成功"。代码逻辑正确。

    四、实例方法 then 微任务的实现

    由于原生的 Promise 是V8引擎提供的微任务,我们无法还原V8引擎的实现,所以这里使用 setTimeout 模拟异步,所以原生的是微任务,这里是宏任务。

    另外 Promise A+ 规范3.1 中也提到了:

    翻译一下

    如果你想实现 promise 的微任务,可以 mutationObserver 替代 seiTimeout 来实现微任务。这里只是模拟异步而已。

    then(onFulfilled, onRejected) {
        if (this.status === Fulfilled) {
            if (onFulfilled && typeof onFulfilled === 'function') {
                setTimeout(() => {
                    onFulfilled(this.value)
                }, 0)
            }
        }
        if (this.status === Rejected) {
            if (onRejected && typeof onRejected === 'function') {
                setTimeout(() => {
                    onRejected(this.reason)
                }, 0)
            }
        }
        if (this.status === Pending) {
            if (onFulfilled && typeof onFulfilled === 'function') {
                this.onFulfilled.push(() => {
                    setTimeout(() => {
                        onFulfilled(this.value)
                    }, 0)
                })
            }
            if (onRejected && typeof onRejected === 'function') {
                this.onRejected.push(() => {
                    setTimeout(() => {
                        onRejected(this.reason)
                    }, 0)
                })
            }
        }
    }
    

    五、实例方法 then 链式调用的实现

    实例方法 then 链式调用有两个要求:

    • 在实例方法 then 后面可以直接使用实例方法 then

    • 在前面一个实例方法 then 返回一个值,不管是什么值,在后面一个实例方法 then 中都能获取到。

    其实这种链式调用的实现很简单,在实例方法 then 返回一个新的 Promise 对象,把实例方法 then 返回的值 value,通过 resolve(value)reject(value) 传递出去。

    这个功能实现的难点是对实例方法 then 返回的值的类型的判断以及对应的处理。所以要写一个工具函数 handleValue 来专门处理实例方法 then 返回的值。

    then(onFulfilled, onRejected) {
        let promise = new Promise((resolve, reject) => {
            if (this.status === Fulfilled) {
                if (onFulfilled && typeof onFulfilled === 'function') {
                    setTimeout(() => {
                        let x = onFulfilled(this.value);
                        handleValue(promise,x,resolve,reject);
                    }, 0)
                }
            }
            if (this.status === Rejected) {
                if (onRejected && typeof onRejected === 'function') {
                    setTimeout(() => {
                        let x = onRejected(this.reason);
                        handleValue(promise,x,resolve,reject);
                    }, 0)
                }
            }
            if (this.status === Pending) {
                if (onFulfilled && typeof onFulfilled === 'function') {
                    this.onFulfilled.push(() => {
                        setTimeout(() => {
                            let x = onFulfilled(this.value);
                            handleValue(promise,x,resolve,reject);
                        }, 0)
                    })
                }
                if (onRejected && typeof onRejected === 'function') {
                    this.onRejected.push(() => {
                        setTimeout(() => {
                            let x = onRejected(this.reason);
                            handleValue(promise,x,resolve,reject);
                        }, 0)
                    })
                }
            }
        })
        return promise
    }
    

    如上,改造后实例方法 then 已经可以实现链式调用了。但是还没实现前一个实例方法 then 返回一个值,在后面一个实例方法 then 中能获取到。接下来在 handleValue 函数中实现。

    按照 Promise/A+ 规范中对不同类型的返回值 X 的处理规则来实现 handleValue 函数,注意看代码注释。

    const handleValue = (promise, x, resolve, reject) => {
        // 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
        if (promise === x) {
            return reject(new TypeError('检测到Promise的链式循环引用'))
        }
        // 确保只传递出去一次值
        let once = false;
        if ((x !== null && typeof x === 'object') || typeof x === 'function') {
            // 防止重复去读取x.then
            let then = x.then;
            // 判断x是不是Promise
            if (typeof then === 'function') {
                //调用then实例方法处理Promise执行结果
                then.call(x, y => {
                    if (once) return;
                    once = true;
                    // 防止Promise中Promise执行成功后又传递一个Promise过来,
                    // 要做递归解析。
                    handleValue(promise, y, resolve, reject);
                }, r => {
                    if (once) return;
                    once = true;
                    reject(r);
                })
            } else {
                // 如果x是个普通对象,直接调用resolve(x)
                resolve(x);
            }
        } else {
            // 如果x是个原始值,直接调用resolve(x)
            resolve(x);
        }
    }
    

    在上述代码中,判断typeof then === 'function'时其实是在判断返回的 x 是否为一个 Promise。如果没有 then 函数,x 即为普通值,直接返回 resolve(x)。如果有 then 函数,x 即为一个 Promise,就递归解析这个 Promise,直到 x 是一个普通值后作为最后的结果返回。

    那么为什么用typeof then === 'function' 判断 x 是否为一个 Promise ,而不是用 x instanceof Promise 。 这是为了让 Promise 更具有通用性,所以一个 thenable 对象也可以看做是一个 Promise 。 thenable 对象就是一个拥有 then 方法的对象,如下代码所示例:

    let thenable = {
        then: function(resolve, reject){
        	resolve('执行成功')
        }
    }
    

    thenable.then 方法中通过 resolve 传递执行成功的结果。但是 thenable 对象不是通过 Promise 类 new 出来的,故不能通过 x instanceof Promise 来判断是不是一个 Promise。

    另外在Promise A+ 规范1.1 中也提到了:Promise 是一个具有 then 方法的对象或函数,其行为符合此规范。

    六、实例方法 then 值穿透的实现

    在上面的实现方法 then 链式调用的实现过程中,已经实现了值传递,当然是在 then 有传入参数的场景下。

    那么在实例方法 then 中没传入参数,例:

    const test = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('"执行成功"')
        }, 3000)
    
    })
    test.then().then(res =>{
        console.log(res)
    })
    

    此时后面的实例方法 then 依旧可以得到之前实例方法 then 返回的值,这就是所谓的值的穿透。正如上述例子中,控制台还是可以打印出 "执行成功"。

    简单修改一下实例方法 then 就可以实现。

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };;
        let promise = new Promise((resolve, reject) => {
            if (this.status === Fulfilled) {
                setTimeout(() => {
                    let x = onFulfilled(this.value);
                    handleValue(promise, x, resolve, reject);
                }, 0)
            }
            if (this.status === Rejected) {
                if (onRejected && typeof onRejected === 'function') {
                    setTimeout(() => {
                        let x = onRejected(this.reason);
                        handleValue(promise, x, resolve, reject);
                    }, 0)
                }
            }
            if (this.status === Pending) {
                this.onFulfilled.push(() => {
                    setTimeout(() => {
                        let x = onFulfilled(this.value);
                        handleValue(promise, x, resolve, reject);
                    }, 0)
                })
                if (onRejected && typeof onRejected === 'function') {
                    this.onRejected.push(() => {
                        setTimeout(() => {
                            let x = onRejected(this.reason);
                            handleValue(promise, x, resolve, reject);
                        }, 0)
                    })
                }
            }
        })
        return promise
    }
    

    七、Promise 内部执行错误处理

    上述代码都没对 Promise 内部执行错误进行捕获,可以用 try ... catch 语句来捕获错误,把错误用内置方法传递出去 reject,防止 Promise 内部执行错误无法追踪。

    // 在这里用Symbol定义三种状态,防止外部改变状态
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    const handleValue = (promise, x, resolve, reject) => {
        // 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
        if (promise === x) {
            return reject(new TypeError('检测到Promise的链式循环引用'))
        }
        // 确保只传递出去一次值
        let once = false;
        if ((x !== null && typeof x === 'object') || typeof x === 'function') {
            try {
                // 防止重复去读取x.then
                let then = x.then;
                // 判断x是不是Promise
                if (typeof then === 'function') {
                    //调用then实例方法处理Promise执行结果
                    then.call(x, y => {
                        if (once) return;
                        once = true;
                        // 防止Promise中Promise执行成功后又传递一个Promise过来,
                        // 要做递归解析。
                        handleValue(promise, y, resolve, reject);
                    }, r => {
                        if (once) return;
                        once = true;
                        reject(r);
                    })
                } else {
                    // 如果x是个普通对象,直接调用resolve(x)
                    resolve(x);
                }
            } catch (err) {
                if (once) return;
                once = true;
                reject(err);
            }
        } else {
            // 如果x是个原始值,直接调用resolve(x)
            resolve(x);
        }
    }
    class Promise {
        constructor(executor) {
            this.status = Pending; //存储 Promise 的状态
            this.value = undefined; //存储executor函数中业务代码执行成功的结果
            this.reason = undefined; //存储executor函数中业务代码执行失败的原因
            this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
            this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
            const resolve = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Fulfilled;
                    this.value = value;
                    // 依次调用成功回调函数
                    this.onFulfilled.forEach(fn => fn());
                }
            };
            const reject = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Rejected;
                    this.reason = value;
                    // 依次调用失败回调函数
                    this.onRejected.forEach(fn => fn());
                }
            };
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(error)
            }
    
        }
        then(onFulfilled, onRejected) {
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
            onRejected = typeof onRejected === 'function' ? onRejected : err => {
                throw err
            };;
            let promise = new Promise((resolve, reject) => {
                if (this.status === Fulfilled) {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            handleValue(promise, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                }
                if (this.status === Rejected) {
                    if (onRejected && typeof onRejected === 'function') {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    }
                }
                if (this.status === Pending) {
                    this.onFulfilled.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                    if (onRejected && typeof onRejected === 'function') {
                        this.onRejected.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onRejected(this.reason);
                                    handleValue(promise, x, resolve, reject);
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                }
            })
            return promise
        }
    }
    

    八、测试 Promise 是否符合规范

    有专门的测试脚本可以测试所编写的代码是否符合 PromiseA+ 规范。

    安装测试脚本:

    npm install -g promises-aplus-tests
    
    复制以下代码到 promise.js 文件中
    // 在这里用Symbol定义三种状态,防止外部改变状态
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    const handleValue = (promise, x, resolve, reject) => {
        // 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
        if (promise === x) {
            return reject(new TypeError('检测到Promise的链式循环引用'))
        }
        // 确保递归解析中只传递出去一次值
        let once = false;
        if ((x !== null && typeof x === 'object') || typeof x === 'function') {
            try {
                // 防止重复去读取x.then
                let then = x.then;
                // 判断x是不是Promise
                if (typeof then === 'function') {
                    //调用then实例方法处理Promise执行结果
                    then.call(x, y => {
                        if (once) return;
                        once = true;
                        // 防止Promise中Promise执行成功后又传递一个Promise过来,
                        // 要做递归解析。
                        handleValue(promise, y, resolve, reject);
                    }, r => {
                        if (once) return;
                        once = true;
                        reject(r);
                    })
                } else {
                    // 如果x是个普通对象,直接调用resolve(x)
                    resolve(x);
                }
            } catch (err) {
                if (once) return;
                once = true;
                reject(err);
            }
        } else {
            // 如果x是个原始值,直接调用resolve(x)
            resolve(x);
        }
    }
    class Promise {
        constructor(executor) {
            this.status = Pending; //存储 Promise 的状态
            this.value = undefined; //存储executor函数中业务代码执行成功的结果
            this.reason = undefined; //存储executor函数中业务代码执行失败的原因
            this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
            this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
            const resolve = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Fulfilled;
                    this.value = value;
                    // 依次调用成功回调函数
                    this.onFulfilled.forEach(fn => fn());
                }
            };
            const reject = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Rejected;
                    this.reason = value;
                    // 依次调用失败回调函数
                    this.onRejected.forEach(fn => fn());
                }
            };
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(error)
            }
    
        }
        then(onFulfilled, onRejected) {
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
            onRejected = typeof onRejected === 'function' ? onRejected : err => {
                throw err
            };;
            let promise = new Promise((resolve, reject) => {
                if (this.status === Fulfilled) {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            handleValue(promise, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                }
                if (this.status === Rejected) {
                    if (onRejected && typeof onRejected === 'function') {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    }
                }
                if (this.status === Pending) {
                    this.onFulfilled.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                    if (onRejected && typeof onRejected === 'function') {
                        this.onRejected.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onRejected(this.reason);
                                    handleValue(promise, x, resolve, reject);
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                }
            })
            return promise
        }
    	static defer(){
    		let dfd = {};
    		dfd.promise = new Promise((resolve, reject) => {
    			dfd.resolve = resolve;
    			dfd.reject = reject;
    		});
    		return dfd;
    	}
    	static deferred(){
    		let dfd = {};
    		dfd.promise = new Promise((resolve, reject) => {
    			dfd.resolve = resolve;
    			dfd.reject = reject;
    		});
    		return dfd;
    	}
    }
    module.exports = Promise;
    

    那么在对应的目录执行以下命令:

    promises-aplus-tests promise.js
    

    执行结果如下所示 『面试的底气』—— 实现一个Promise

    872 个测试用例全部通过。

    九、实例方法 catch 的实现

    this.catch就是 this.then(null, onRejected)的别名。

    catch(onRejected){
    	this.then(null, onRejected)
    }
    

    十、静态方法 Promise.resolve() 的实现

    先回顾一下 Promise.resolve() 的用法。

    Promise.resolve() 的作用是把传入的参数转成一个 Promise 对象。

    • 如果参数是一个 Promise 实例,直接返回这个 Promise 实例。

    • 如果参数是一个 thenable 对象,

      thenable 对象指的是具有 then 方法的对象,例如

      let thenable = {
      	then(resolve,reject){
          	resolve (42);
          }
      }
      

      Promise.resolve() 方法会将这个对象转为 Promise 对象,然后立即执行 thenable 对象 then 方法。

    • 参数不是具有 then 方法的对象或根本不是对象,那么 Promise.resolve() 方法返回个新的 Promise 实例,状态为已成功,并把参数传递出去。

    • 不带有任何参数,Promise.resolve() 方法允许在调用时不带有参数而直接返回个新的 Promise 实例,状态为已成功。

    根据其的用法很容易实现 Promise.resolve()

    class Promise{
        //...
        static resolve(param) {
            if (param instanceof Promise){
                return param;
            }
            return new Promise((resolve,reject) =>{
                if(
                    param && 
                    Object.prototype.toString.call(param) === '[object Object]' && 
                    typeof param.then === 'function'
                ){
                    setTimeout(() =>{
                        param.then(resolve,reject)
                    },0)
                }else{
                    resolve(param)
                }
            })
        }
    }
    

    十一、静态方法 Promise.reject() 的实现

    先回顾一下 Promise.reject() 的用法:返回一个新的 Promise 实例,状态为已失败,并把参数作为失败的原因传递出去。

    根据其的用法很容易实现 Promise.reject()

    class Promise{
        //...
        static reject(param){
            return new Promise((resolve,reject) =>{
                reject(param)
            })
        }
    }
    

    十二、静态方法 Promise.all() 的实现

    先回顾一下 Promise.all() 的用法。

    Promise.all() 的作用是把多个 Promise 实例包装成一个新的 Promise 实例。

    例如:p = Promise.all(p1,p2,p3),其中 p1、p2、p3 不是 Promise 实例的,内部会通过 Promise.resolve() 将其转成 Promise 实例。p 的状态由 p1、p2、p3 决定, 分成两种情况。

    • 只有 p1、p2、p3 的状态都变为已成功, p 的状态才会变为已成功 ,此 pl p2 p3 的返回值组成一个数组,传递给 p 的回调函数。

    • 只要 pl p2 p3 中有一个的状态变为已失败,p 的状态就会变为已失败,此时 pl p2 p3 中第一个状态变为已失败的返回值会

    传递给 p 的回调函数。

    class Promise {
        //...
        static all(promises) {
            //将参数promises转为一个真正的数组
            promises = Array.from(promises);
            return new Promise((resolve, reject) => {
                let value = [];
                const length = promises.length;
                if (length) {
                    for (let i = 0; i < length; i++) {
                        Promise.resolve(promises[i]).then(
                            res => {
                                value.push(res);
                                if (value.length == length) {
                                    resolve(value);
                                }
                            },
                            err => {
                                reject(err)
                                return;
                            }
                        )
                    }
                } else {
                    resolve(value)
                }
            })
        }
    }
    

    十三、静态方法 Promise.race() 的实现

    先回顾一下 Promise.race() 的用法。

    Promise.race() 的作用是把多个 Promise 实例包装成一个新的 Promise 实例。

    例如:p = Promise.all(p1,p2,p3),其中 p1、p2、p3 不是 Promise 实例的,内部会通过 Promise.resolve() 将其转成 Promise 实例。p 的状态由 p1、p2、p3 决定, 只要 pl p2 p3 中有一个的状态改变,p 的状态马上就会对应改变,此时 pl p2 p3 中第一个状态改变的返回值会传递给 p 的回调函数。

    class Promise {
        //...
        static race(promises) {
            //将参数promises转为一个真正的数组
            promises = Array.from(promises);
            return new Promise((resolve, reject) => {
                const length = promises.length;
                if (length) {
                    for (let i = 0; i < length; i++) {
                        Promise.resolve(promises[i]).then(
                            res => {
                                resolve(res);
                                return;
                            },
                            err => {
                                reject(err)
                                return;
                            }
                        )
                    }
                } else {
                    resolve(value)
                }
            })
        }
    }
    

    十四、完整代码

    // 在这里用Symbol定义三种状态,防止外部改变状态
    const Pending = Symbol('Pending'); // 进行中
    const Fulfilled = Symbol('Fulfilled'); // 已成功
    const Rejected = Symbol('Rejected'); // 已失败
    const handleValue = (promise, x, resolve, reject) => {
        // 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
        if (promise === x) {
            return reject(new TypeError('检测到Promise的链式循环引用'))
        }
        // 确保递归解析中只传递出去一次值
        let once = false;
        if ((x !== null && typeof x === 'object') || typeof x === 'function') {
            try {
                // 防止重复去读取x.then
                let then = x.then;
                // 判断x是不是Promise
                if (typeof then === 'function') {
                    //调用then实例方法处理Promise执行结果
                    then.call(x, y => {
                        if (once) return;
                        once = true;
                        // 防止Promise中Promise执行成功后又传递一个Promise过来,
                        // 要做递归解析。
                        handleValue(promise, y, resolve, reject);
                    }, r => {
                        if (once) return;
                        once = true;
                        reject(r);
                    })
                } else {
                    // 如果x是个普通对象,直接调用resolve(x)
                    resolve(x);
                }
            } catch (err) {
                if (once) return;
                once = true;
                reject(err);
            }
        } else {
            // 如果x是个原始值,直接调用resolve(x)
            resolve(x);
        }
    }
    class Promise {
        constructor(executor) {
            this.status = Pending; //存储 Promise 的状态
            this.value = undefined; //存储executor函数中业务代码执行成功的结果
            this.reason = undefined; //存储executor函数中业务代码执行失败的原因
            this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
            this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
            const resolve = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Fulfilled;
                    this.value = value;
                    // 依次调用成功回调函数
                    this.onFulfilled.forEach(fn => fn());
                }
            };
            const reject = value => {
                // 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
                if (this.status === Pending) {
                    this.status = Rejected;
                    this.reason = value;
                    // 依次调用失败回调函数
                    this.onRejected.forEach(fn => fn());
                }
            };
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(error)
            }
    
        }
        then(onFulfilled, onRejected) {
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
            onRejected = typeof onRejected === 'function' ? onRejected : err => {
                throw err
            };;
            let promise = new Promise((resolve, reject) => {
                if (this.status === Fulfilled) {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            handleValue(promise, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                }
                if (this.status === Rejected) {
                    if (onRejected && typeof onRejected === 'function') {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    }
                }
                if (this.status === Pending) {
                    this.onFulfilled.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value);
                                handleValue(promise, x, resolve, reject);
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    })
                    if (onRejected && typeof onRejected === 'function') {
                        this.onRejected.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onRejected(this.reason);
                                    handleValue(promise, x, resolve, reject);
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                }
            })
            return promise
        }
        static resolve(param) {
            if (param instanceof Promise){
                return param;
            }
            return new Promise((resolve,reject) =>{
                if(
                    param && 
                    Object.prototype.toString.call(param) === '[object Object]' && 
                    typeof param.then === 'function'
                ){
                    setTimeout(() =>{
                        param.then(resolve,reject)
                    },0)
                }else{
                    resolve(param)
                }
            })
        }
        static reject(param){
            return new Promise((resolve,reject) =>{
                reject(param)
            })
        }
        static all(promises) {
            //将参数promises转为一个真正的数组
            promises = Array.from(promises);
            return new Promise((resolve, reject) => {
                let value = [];
                const length = promises.length;
                if (length) {
                    for (let i = 0; i < length; i++) {
                        Promise.resolve(promises[i]).then(
                            res => {
                                value.push(res);
                                if (value.length == length) {
                                    resolve(value);
                                }
                            },
                            err => {
                                reject(err)
                                return;
                            }
                        )
                    }
                } else {
                    resolve(value)
                }
            })
        }
        static race(promises) {
            //将参数promises转为一个真正的数组
            promises = Array.from(promises);
            return new Promise((resolve, reject) => {
                const length = promises.length;
                if (length) {
                    for (let i = 0; i < length; i++) {
                        Promise.resolve(promises[i]).then(
                            res => {
                                resolve(res);
                                return;
                            },
                            err => {
                                reject(err)
                                return;
                            }
                        )
                    }
                } else {
                    resolve(value)
                }
            })
        }
    }
    

    起源地下载网 » 『面试的底气』—— 实现一个Promise

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元