• 手写Promise过程记录


    参考资料

    Promises/A+规范中文

    BAT前端经典面试问题:史上最最最详细的手写Promise教程(很详细,一步一步来,还有catch和resolve、reject、race、all方法,末尾有验证手写Promise正确性的代码)

    手摸手教你实现Promise/A+规范(视频教程)

    实现的promise文件

    准备三个文件

    • index.js:原生的Promise
    • promise.js:手写Promise
    • test.js:测试promise.js是否正确

    1.声明Promise

    • promise是一个类,所以使用class声明

    • 构造器constructor接收函数参数executor

    • 开始前先进行,参数校验,输入参数不是函数时,抛出TypeError错误

    • executor中有两个参数,分别是resolvereject,分别定义后,再传递给executor

    class Promise {
        // 构造器接收函数参数executor
        constructor(executor) {
            // 参数校验
            if (typeof executor !== 'function') {
                throw new TypeError(`Promise resolver ${executor} is not a function`)
            }
    
             const resolve = function(){}
             const reject = function(){}
    
             // 传递给executor并执行
             executor(resolve, reject)
        }
    }
    module.exports = Promise;
    
    // [test.js]
    const Promise = require('./promise.js')
    new Promise((resolve, reject) => {
        console.log('开始')
        resolve(1)
    })
    

    2.resolve和reject

    resolve: promise 成功时进行的一系列操作,如状态的改变、成功回调的执行(即Promise/A+中的解决fulfill

    reject:promise 失败时进行的一系列操作,如状态的改变、失败回调的执行

    value:终值(eventual value),promise 被解决时传递给解决回调的值。这个值被传递时,标志着等待状态的结束

    reason:拒因,promise 被拒绝时传递给拒绝回调的值

    一次性特征:Promise必须由等待状态迁移至执行态/拒绝态,状态改变后不可再次改变。

    需要定义值state记录状态的改变。

    • Pending状态不可逆,在resolve和reject中,只有状态为Pending,才能继续执行下去
    class Promise {
        constructor(executor) {
            // 参数校验
            // ......
            
            // 初始化值
            this.value = null // 终值
            this.reason = null // 拒因
            this.state = Promise.PENDING // 状态(不可逆)
    
            // 定义resolve
            const resolve = function(value){
                // 成功后的一系列操作(状态改变,成功回调的执行)
                if (this.state === Promise.PENDING) {
                    // 状态变化为成功
                    this.state = Promise.FULFILLED
                    // 值的变化
                    this.value = value
                }
            }
    
            // 定义reject
            const reject = function(reason){
                // 失败后的一系列操作(状态改变,失败回调的执行)
                if (this.state === Promise.PENDING) {
                    // 状态变化为拒绝
                    this.state = Promise.REJECTD
                    // 值的变化
                    this.reason = reason
                }
            }
            
            // 传递给executor并执行
            // ......
    	}
    }
    
    Promise.PENDING = 'pending'
    Promise.FULFILLED = 'fulfilled'
    Promise.REJECTD = 'rejected'
    
    // [test.js]
    // TypeError:Cannot read property 'state' of undefined
    // 调用resovle/reject时是匿名调用,此时this=undefined  ---->  使用箭头函数
    
    // 定义resolve
    const resolve = value => {
        // ......
    }
    
    // 定义reject
    const reject = reason => {
        // ......
    }
    

    优化:

    • 初始化值单独提取出来initValue()
    • resolve和reject提取作为类的方法
    class Promise {
        // 接收函数参数executor
        constructor(executor) {
            // 参数校验
            // ......
    
            this.initValue()
    
            // 传递给executor并执行
            executor(this.resolve, this.reject)
        }
    
        // 初始化值
        initValue() {
            this.value = null // 终值
            this.reason = null // 拒因
            this.state = Promise.PENDING // 状态(不可逆)
        }
    
        // 定义resolve
        resolve(value) {
            if (this.state === Promise.PENDING) {
                this.state = Promise.FULFILLED
                this.value = value
            }
        }
    
        // 定义reject
        reject(reason) {
            if (this.state === Promise.PENDING) {
                this.state = Promise.REJECTD
                this.reason = reason
            }
        }
    }
    
    // [test.js]
    // TypeError:Cannot read property 'state' of undefined
    // 调用resovle/reject时是匿名调用,此时this=undefined  ---->  bind绑定
    
    class Promise {
        // 接收函数参数executor
        constructor(executor) {
            // 参数校验
            // ......
    
            this.initValue()
            this.initBind()
    
            // 传递给executor并执行
            executor(this.resolve, this.reject)
        }
        
        // 绑定this
        initBind() {
            this.resolve = this.resolve.bind(this)
            this.reject = this.reject.bind(this)
        }
        // ......
    }
    

    3.then

    promise.then(onFulfilled, onRejected)

    参数可选,如果不是函数,其必须被忽略------>参数判断,如果不是函数,并不是完全忽略。

    // [index.js]
    new Promise((resolve,reject)=>{
        console.log('开始')
        resolve(1)
    })
    .then()
    .then(value=>{
        console.log('value',value)
    },reason=>{
        console.log('reason',reason)
    })
    
    /* 
        then()不是函数,什么都不传。
        输出仍为 开始 value 1 [忽略]
    */
    
    // 定义then
    then(onFulfilled, onRejected) {
        // 参数校验  不是函数-->穿透效果(连续调用then)
        if (typeof onFulfilled !== 'function') {
            // 重新返回value
            onFulfilled = function (value) {
                return value
            }
        }
        
        if (typeof onRejected !== 'function') {
            // 重新抛出reason
            onRejected = function (reason) {
                throw reason
            }
        }
    }
    

    onFulfilled

    状态为fulfilled时,传入this.value。

    promise执行结束后必须被调用,结束前不可被调用。调用次数不能超过一次。

    onRejected

    状态为rejected时,传入this.reason。

    promise被拒绝执行后必须被调用,被拒绝结束前不可被调用。调用次数不能超过一次。

    // 定义then
    then(onFulfilled, onRejected) {
        // ......
        if (this.state === Promise.FULFILLED) {
            onFulfilled(this.value)
        }
        if (this.state === Promise.REJECTD) {
            onRejected(this.reason)
        }
    }
    

    4.异步任务执行顺序

    // 执行栈->微任务->宏任务
    // 当主栈第一阶段执行完,查看是否有微任务队列,先执行微任务中所有任务,再执行宏任务队列
    console.log('1') // 执行主栈输出1
    new Promise((resolve, reject) => {
        console.log('2') // executor立即执行,主栈输出2
        resolve(1) // 执行栈,then中的resolve和reject属于微任务
    }).then(value => {
       console.log('4') // 微任务输出4 
       console.log('value', value) // 微任务输出value 1
    }, reason => {
        console.log('reason', reason)
    })
    console.log('3') // 执行栈输出3
    

    [index.js]输出

    [test.js]输出,onFulfilled函数立即执行了

    image-20200915175624237

    if (this.state === Promise.FULFILLED) {
        // onFulfilled和onRejected不能同时被调用,通过setTimeout异步调用
        setTimeout(() => {
            onFulfilled(this.value)
        })
    }
    
    if (this.state === Promise.REJECTD) {
        setTimeout(() => {
            onRejected(this.reason)
        })
    }
    

    此时,异步任务执行顺序模拟成功了。

    测试在promise中抛出异常

    console.log('1')
    new Promise((resolve, reject) => {
        throw new Error('chucuole')
        resolve(1)
    }).then(value => {
       console.log('4')
       console.log('value', value)
    }, reason => {
        console.log('reason', reason)
    })
    console.log('3')
    
    // [index.js]出错信息放在了reject内的reason为 reason Error:chucuole
    
    // try...catch捕获错误,交给reject
    try {
        // 传递给executor并执行
        executor(this.resolve, this.reject)
    } catch (e) {
        this.reject(e)
    }
    

    测试promise内为异步任务

    // 当主栈第一阶段执行完,查看是否有微任务队列,先执行微任务中所有任务,再执行宏任务队列
    console.log('1')// 执行主栈输出1
    new Promise((resolve, reject) => {
        setTimeout(()=>{ // 放入宏任务队列,EventLoop此时无微任务,执行宏任务
            console.log('haha') 
    		resolve(1) 
        })
    }).then(value => {
       console.log('4') // 微任务输出4 
       console.log('value', value)// 微任务输出value 1
    }, reason => {
        console.log('reason', reason)
    })
    console.log('3') // 执行主栈输出3
    
    //[index.js] 1 3 haha 4 value 1
    //[test.js] 1 3 haha
    

    原因:setTimeout()没有执行,就直接执行了then。(还没懂)state的状态一直是pending,所以没有进入resolve和reject中。

    解决:

    • 添加两个数组onFulfilledCallbacks和onRejectedCallbacks
    • 将成功resolve和失败reject存到各自数组
    • 一旦resolve或reject就调用它们
    // 初始化值
    initValue() {
        // ......
        this.onFulfilledCallbacks = [] // 成功回调
        this.onRejectedCallbacks = [] // 失败回调
    }
    
    // 定义resolve
    resolve(value) {
        if (this.state === Promise.PENDING) {
            this.state = Promise.FULFILLED
            this.value = value
            // 一旦resolve执行,调用成功数组的函数
            this.onFulfilledCallbacks.forEach(fn => fn(this.value))
        }
    }
    
    // 定义reject
    reject(reason) {
        if (this.state === Promise.PENDING) {
            this.state = Promise.REJECTD
            this.reason = reason
            // 一旦reject执行,调用失败数组的函数
            this.onRejectedCallbacks.forEach(fn => fn(this.reason))
        }
    }
    
    // 状态为pending时,把onFulfilled和onRejected传入数组
    if (this.state === Promise.PENDING) {
        this.onFulfilledCallbacks.push((value) => {
            setTimeout(() => {
                onFulfilled(this.value)
            })
        })
    
        this.onRejectedCallbacks.push((reason) => {
            setTimeout(() => {
                onRejected(this.reason)
            })
        })
    }
    

    5.链式调用

    为了实现链式调用,且改变后面then的值,必须通过新的实例。

    默认在第一个then中返回一个promise,即在then中返回新的promise(promise2)。

    • 将这个promise2返回的值传递给下一个then
    • 如果返回一个普通的值,则将普通的值传递给下一个then

    具体实现中需要对第一个then返回的值(x)进行判断,通过定义resolvePromise判断。

    let promise2 = new Promise((resolve, reject) => {
        
    })
    return promise2
    
    let promise2 = new Promise((resolve, reject) => {
    
        if (this.state === Promise.FULFILLED) {
            setTimeout(() => {
                const x = onFulfilled(this.value)
                Promise.resolvePromise(promise2, x, resolve, reject)
            })
        }
    
        if (this.state === Promise.REJECTD) {
            setTimeout(() => {
                const x = onRejected(this.reason)
                Promise.resolvePromise(promise2, x, resolve, reject)
            })
        }
    
        if (this.state === Promise.PENDING) {
            this.onFulfilledCallbacks.push((value) => {
                setTimeout(() => {
                    const x = onFulfilled(this.value)
                    Promise.resolvePromise(promise2, x, resolve, reject)
                })
            })
    
            this.onRejectedCallbacks.push((reason) => {
                setTimeout(() => {
                    const x = onRejected(this.reason)
                    Promise.resolvePromise(promise2, x, resolve, reject)
                })
            })
        }
    })
    return promise2
    
    // 判断x的函数 (默认返回的promise,自己return的对象,promise2的resolve,promise2的reject)
    Promise.resolvePromise = function (promise2, x, resolve, reject) {}
    

    当onFulfilled和onRejected抛出异常时,promise2拒绝执行,并返回拒因

    try...catch捕获异常,catch中reject。

    if (this.state === Promise.FULFILLED) {
        setTimeout(() => {
            try {
                const x = onFulfilled(this.value)
                Promise.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
                reject(e)
            }
        })
    }
    // 其他类似
    

    6.链式调用中判断x的resolvePromise

    判断x时有以下4种情形。

    1. x 与 promise2 相等,循环调用
    2. x为Promise,取它的结果作为新的promise2成功的结果。x的值为promise时,必须等待该promise执行结束,再进行。
    3. x为对象或函数
    4. x为普通值,直接作为promise2成功的结果
    • x===promise2
    // [index.js] 循环调用,自己等待自己完成
    let p1 = new Promise(resolve=>{
        resolve(1)
    })
    let p2 = p1.then(()=>{
        return p2
    })
    // TypeError:Chaining cycle detected for promise
    
    Promise.resolvePromise = function (promise2, x, resolve, reject) {
        // x 与 Promise 相等
        if (promise2 === x) {
            // 避免循环调用
            reject(new TypeError('Chaining cycle detected for promise'))
        }
    }
    
    • x为promise
    if (x instanceof Promise) {
        x.then(value => {
            Promise.resolvePromise(promise2, value, resolve, reject)
        }, reason => {
            reject(reason)
        })
    }
    
    • x为对象或函数
      • 判断有没有then方法,有的就默认为promise
      • 没有的就是普通的对象
    // null是object,需要另外排除
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // 如果then是函数,默认为promise
            if (typeof x.then === 'function') {
                x.then(value => {
                    Promise.resolvePromise(promise2, value, resolve, reject)
                }, reason => {
                    reject(reason)
                })
            } else {
                // 普通对象
                resolve(x)
            }
        } catch (e) {
            // 取then出错
            reject(e)
        }
    }
    

    成功和失败只能调用一个,定义called防止多次调用

    // 定义变量,是否被调用过
    let called = false
    
    // null是object,需要另外排除
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            if (typeof x.then === 'function') {
                x.then(value => {
                    if (called) return
                    called = true
                    Promise.resolvePromise(promise2, value, resolve, reject)
                }, reason => {
                    if (called) return
                    called = true
                    reject(reason)
                })
            } else {
                if (called) return
                called = true
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    }
    
    • x为普通值
    else {
        // 普通值
        resolve(x)
    }
    

    7.测试

    安装插件npm i promises-aplus-tests -g ,在自己的promise.js末尾添上以下代码。

    Promise.defer = Promise.deferred = function () {
        let dfd = {}
        dfd.promise = new Promise((resolve, reject) => {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }
    

    测试:npx promises-aplus-tests promise.js

    8.debug

    Otherwise, if x is an object or function,Let then be x.then

    • 使用then代替x.then,使用call修改this指向
    const then = x.then
    if (typeof then === 'function') {
        then.call(x, (value) => {
            if (called) return
            called = true
            Promise.resolvePromise(promise2, value, resolve, reject)
    
        }, reason => {
            if (called) return
            called = true
            reject(reason)
        })
    }
    
  • 相关阅读:
    13-计算属性和侦听器
    12-指令系统介绍
    11-vue的使用
    10-vue的介绍
    09-babel
    08-webpack的介绍
    07-nodejs中npm的使用
    06-Nodejs介绍
    05-面向对象
    Docker结合Jenkins构建持续集成环境
  • 原文地址:https://www.cnblogs.com/wattmelon/p/13675180.html
Copyright © 2020-2023  润新知