美文网首页
学习笔记(三)—— 手写Promise

学习笔记(三)—— 手写Promise

作者: 彪悍de文艺青年 | 来源:发表于2020-08-26 09:47 被阅读0次

    最近参加拉勾教育大前端高薪训练营,开始对前端知识体系进行一个系统的扩展跟学习,通过每天定期的学习,对一些不常使用的知识点进行了了解和补充,同时对已经使用过的知识点温故而知新
    在此记录学习笔记,并根据学习进度定时跟新

    Promise类核心逻辑实现

    • Promise是一个类,执行时需要传递一个执行器函数,并会立即执行
    • Promsie有三种状态:pending、fulfilled、rejected
      • 状态只能从pending -> fulfilled 或 pending -> rejected
      • 状态一旦确定不能再修改
    • 执行器的resolve、reject 函数参数用来修改状态
      • resolve -> fulfilled
      • reject -> rejected
    • then方法
      • 判断Promise的状态,并执行相应的回调
      • 被定义在原型对象上
      • 调用onFulfilled传递成功执行的结果
      • 调用rejected传递失败的原因

    Promise - then方法实现

    • 增加异步逻辑
      • 保存onFulfilled、onRejected回调
      • 在resolve、reject调用时,判断并执行相应的回调
    • then方法多次调用添加多个处理函数
      • 使用数组保存多个回调,resolve时依次执行
    • then的链式调用
      • then方法返回一个新的Promise(new MyPromise),并将onFulfilled的返回值传递给resolve,onRejected的返回值传递给reject
      • 判断then方法onFulfilled回调返回一个Promise对象,等待该Promise执行完成,并将相应的状态结果传入then方法返回的新Promise对象的resolve、reject
      • 判断then方法onFulfilled回调识别返回Promsie对象自己,判断与当前Promise是否相等,并向reject传入相应的异常
      • 异常捕获及其他补充
        • 执行器函数及then回调执行增加try...catch捕获异常,并传入reject
      • then方法的回调函数可选
        • 提供默认函数 value => value reason => reason 默认函数只有在不传入回调时起作用,而传入非函数时,未作处理
        • 判断onFulfilled、onReject是否为函数,如果不是回调函数,则提供默认函数
          • onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
          • onRejected = typeof onRejected === 'function' ? onRejected : reason => reason

    Promise.all() 方法实现

    • 接收一个Promise对象数组
    • 返回一个新的Promise对象
      • 所有Promise都resolve,返回resolve结果值组成的数组,传入新Promise的resolve
      • 任一Promise reject,返回第一个reject的Promise的状态值,传入新Promise的reject
    • 由Promise类直接调用,为静态方法
    • 遍历传入的数组,判断是否为Promise对象
      • Promise对象,等待执行,fulfilled加入result数组,rejected则调用reject
      • 普通对象,直接加入result数组
    • 等待所有resolve,调用resolve,传入result数组

    Promise.resolve() 方法实现

    • 接收普通对象,包装并返回一个新的Promsie对象
    • 接收Promsie对象,原封不动返回

    finally() 方法实现

    • 接收一个回调函数
    • 不管Promise状态fulfilled还是reject,都会执行finally回调函数
    • 返回一个新的Promise对象
      • 实现上可以直接在内部调用then方法
      • finally可以renturn一个Promise对象,此时需要等待Promise对象执行完成,可以用Promsie.resolve()包装finally的返回值,并等待Promsie执行完成后,在then中resolve或者在reject中throw reason

    catch() 方法实现

    • 接收一个onRejected回调函数
    • then方法onRejected回调的语法糖
    • 实现上可以直接在内部调用then方法,onFulfilled回调函数传入undefined,将onRejected传入then方法

    附:手写MyPromise代码的实现

    // MyPromise.js
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    
    
    const resolvePromise = (promise, value, resolve, reject, status) => {
        if (promise === value) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        if (value instanceof MyPromise) {
            value.then(resolve, reject)
        } else if(status === REJECTED){
            // 课程示例中没有处理reject
            reject(value) 
        } else {
            resolve(value)        
        }
    }
    
    
    const curry = (fn) => {
        return function (...args) {
            if (args.length < fn.length) {
                return function() {
                    return fn(...args, ...arguments)
                }
            }
            return fn(...args)
        }
    }
    
    class AggregateError extends Array {
    
    }
    
    class MyPromise {   
        constructor(executor) {
            try {
                executor(this.resolve, this.reject)
            } catch (e) {
                this.reject(e)
            }
        }
    
        status = PENDING
        value = undefined
        reason = undefined
        onFulfilled = []
        onRejected = []
    
        resolve = value => {
            if (this.status !== PENDING) return 
            this.status = FULFILLED
            this.value = value
            while(this.onFulfilled.length) this.onFulfilled.shift()()            
        }
    
        reject = reason => {
            if (this.status !== PENDING) return 
            this.status = REJECTED
            this.reason = reason
            while(this.onRejected.length) this.onRejected.shift()()
        }
    
        then(onFulfilled, onRejected) {
            // 如果onFulfilled不是一个函数,则忽略之,向下传递
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
            // 如果onRejected不是一个函数,则忽略之,向下传递
            onRejected = typeof onRejected === 'function' ? onRejected : reason => reason;
    
            
            let newPromise = new MyPromise((resolve, reject) => {
                setTimeout(()=> {
                    // 异常处理部分逻辑提取柯里化重构
                    const exceptionHandler = curry((promise, resolve, reject, handler, value) => {
                        try {
                            resolvePromise(promise, handler(value), resolve, reject, this.status)
                        } catch (e) {
                            reject(e)
                        }
                    })(newPromise, resolve, reject)
    
                    switch(this.status) {
                        case FULFILLED:
                            exceptionHandler(onFulfilled, this.value);                    
                            break
                        case REJECTED:
                            exceptionHandler(onRejected, this.reason);
                            break
                        default:
                            this.onFulfilled.push(()=>{
                                exceptionHandler(onFulfilled, this.value);
                            })
                            this.onRejected.push(()=> {
                                exceptionHandler(onRejected, this.reason);
                            })
                    }
                }, 0)
            })
            return newPromise
        }
        
        catch (callback) {
            return this.then(undefined, callback)
        }
    
        finally (callback) {
            return this.then(value => {
                return MyPromise.resolve(callback()).then(()=> value)
            }, reason => {
                return MyPromise.resolve(callback()).then(()=> {throw reason})
            })
        }
    
        
    
        /* 课程内容外其他api实现 */
    
        // Promise.resolve(value)
        // 类直接调用 静态方法
        // 返回一个Promise实例,状态为fulfilled
        // 参数是一个Promise实例,直接原封不动返回
        // 参数是一个thenable对象,将thenable对象转换为Promise对象,并立即执行then方法
        // 参数是一个普通对象,作为新Promise实例resolve回调的参数
        static resolve (value) {
            if (value instanceof MyPromise) {
                return value
            } else if(value && typeof value === 'object' && typeof value.then === 'function') {
                // node 环境测试发现Promise是pending状态,而web环境是fulfilled转改,存在差异?
                return new MyPromise(value.then)
            } else {
                return new MyPromise((resolve) => resolve(value))
            }
        }
    
        // Promise.reject(reason)
        // 类直接调用 静态方法
        // 返回一个Promise实例,状态为rejected
        // 参数reason无论传入什么类型,都会直接作为新生成的Promise实例的reject回调函数的参数    
        static reject (reason) {        
            return new MyPromise((resolve, reject) => reject(reason))        
        }
    
        // Promise.all()
        // 类直接调用 静态方法
        // 接收Promise实例数组,如果不是Promise实例,会使用Promise.resolve()进行转换
        // 返回一个新的Promise实例
        // 所有Promise实例resolve,将resolve后的值数组传入新Promise实例的resolve
        // 任意一个Promise实例reject,将最先reject的Promise实例的reason传入新Promise实例的reject
        // 参数传入非数组的情况
        // 字符串及实现了iterator接口的其他类型对象: 转换为字符数组
        // 其他:抛出TypeError
        static all (array) {
            let result = []
            
            // 尝试将参数转化为数组,无法转化的其他对象会抛出异常,此处不做异常捕捉处理        
            array = [...array]
    
            return new MyPromise((resolve, reject) => {
                const put = (key, value) => {
                    result[key] = value
                    result.length === array.length && resolve(result)
                }
                array.forEach((p, idx) => {
                    // 无论是否为Promise实例,都使用resolve进行包装
                    // resolve的加入result数组,reject的直接reject
                    MyPromise.resolve(p).then(value => put(idx, value), reject)                
                })
            })
        }    
    
        // Promise.race()
        // 类直接调用 静态方法
        // 接收Promise实例数组,如果不是Promise实例,会使用Promise.resolve()进行转换
        // 返回一个新的Promise实例
        // 任意一个Promise实例状态发生变化,就将该实例的状态变化作为返回的新Promise实例的状态返回
        // 参数传入非数组的情况
        // 字符串及实现了iterator接口的其他类型对象: 转换为字符数组
        // 其他:抛出TypeError
        static race (array) {
            // 尝试将参数转化为数组,无法转化的其他对象会抛出异常,此处不做异常捕捉处理        
            array = [...array]
            return new MyPromise((resolve, reject) => {
                array.forEach(p => {
                    // 无论是否为Promise实例,都使用resolve进行包装
                    // 由于Promise状态一旦确定即不可变更,最先调用then的实例返回的状态及相应的值即为返回Promise实例的状态
                    MyPromise.resolve(p).then(resolve, reject)
                })
            })
        }
    
        // Promise.any()
        // 类直接调用 静态方法
        // 接收Promise实例数组,如果不是Promise实例,会使用Promise.resolve()进行转换
        // 返回一个新的Promise实例
        // 所有Promise实例reject,将会抛出AggregateError,将所有实例reject的reason组成Error数组,传入新Promise实例的reject
        // 任意一个Promise实例resolve,将最先resolve的Promise实例的返回值传入新Promise实例的resolve
        // 参数传入非数组的情况
        // 字符串及实现了iterator接口的其他类型对象: 转换为字符数组
        // 其他:抛出TypeError
        static any (array) {
            let errors = []
            
            // 尝试将参数转化为数组,无法转化的其他对象会抛出异常,此处不做异常捕捉处理        
            array = [...array]
    
            return new MyPromise((resolve, reject) => {
                const put = (key, reason) => {
                    errors[key] = reason
                    if (errors.length === array.length) {
                        const err = new AggregateError()
                        errors.forEach(e => {
                            err.push(e)
                        })
                        reject(err)
                    }
                }
                array.forEach((p, idx) => {
                    // 无论是否为Promise实例,都使用resolve进行包装
                    // resolve的加入result数组,reject的直接reject
                    MyPromise.resolve(p).then(resolve, reason => put(idx, reason))                
                })
            })
        }
    
        // Promise.allSettled()
        // 类直接调用 静态方法
        // 接收Promise实例数组,如果不是Promise实例,会使用Promise.resolve()进行转换
        // 返回一个新的Promise实例,状态是fulfilled
        // 所有Promise实例都执行完成才会resolve
        // 传入resolve的result是一个数组,每个成员是一个对象,包含status属性指明fulfilled或者rejected
        // fulfilled有value属性,rejected有reason属性
        // 参数传入非数组的情况
        // 字符串及实现了iterator接口的其他类型对象: 转换为字符数组
        // 其他:抛出TypeError
        static allSettled (array) {
            let result = []
            // 尝试将参数转化为数组,无法转化的其他对象会抛出异常,此处不做异常捕捉处理        
            array = [...array]
    
            return new MyPromise(resolve => {
                const put = (key, value) => {
                    result[key] = value
                    result.length === array.length && resolve(result)
                }
                array.forEach((p, idx) => {
                    // 无论是否为Promise实例,都使用resolve进行包装
                    // resolve的加入result数组,reject的直接reject
                    MyPromise.resolve(p).then(value => put(idx, {status: 'fulfilled', value}), reason => put(idx, {status: 'rejected', reason}))
                })
            })
        }
    }
    
    module.exports = MyPromise
    

    相关文章

      网友评论

          本文标题:学习笔记(三)—— 手写Promise

          本文链接:https://www.haomeiwen.com/subject/lzjzjktx.html