Promise自己实现

101 阅读2分钟

Promise

  • promise是一个函数,new 对象使用,返回resolve和reject两个成功、失败的回调函数

    new Promise((resolve,reject)=>{
        console.log(1)
        resolve(2)
        console.log(3)
    }).then((res=>{
        console.log(res)
    }).catch(()=>{
        console.log('err')
    })
            
    // 打印 1 3 2
    
  • 在本轮事件循环运行完成之前,回调函数是不会执行的

  • 及时异步操作已经完成,在这之后通过then添加的回调函数也会被调用

  • 通过多次调用then可以添加多个回调函数,他们会按照插入顺序链式调用,都是一起加入微任务队列

    Promise.resolve()
        .then(() => {
            console.log(1);
            setTimeout(()=>console.log(4))
        })
    Promise.resolve()
        .then(() => console.log(2))
        .then(() => console.log(3));
    
    // 打印  1 2 3 4
    
    const transformData = composeAsync(fn1,fn2,fn3)
    const result = transformData(data)
    // 实现composeAsync,其中fn1,fn2,fn3是返回promise的函数,参数依次接受前面返回值,fn1的参数是data
    function composeAsync(...args){
        return data => args.reduce((p,c)=>{
           return p.then(c)
        },Promise.resolve(data))
    }
    // ==>
    const asyncFn = (p,c)=>p.then(c)
    const composeAsync = (...args) => data => args.reduce(asyncFn,Promise.resolve(data))
    
  • 一旦遇到失败,会顺着promise寻找下一个onRejected失败回调或者.catch()指定的回调函数

  • 当promise被拒绝时,会派发到全局事件,如果有reject处理事件是 rejectionhandled ,没有reject处理事件是 unhandledrejection

    window.addEventListener("unhandledrejection", event => {
      /* 你可以在这里添加一些代码,以便检查
         event.promise 中的 promise 和
         event.reason 中的 rejection 原因 */
    
      event.preventDefault(); // 默认操作一般会包含把错误打印到控制台,Node 就是如此的
    }, false);
    
  • Promise.resolve 和 Promies.reject 是手动创建resolve和reject的promise方法

  • Promise.all 和 Promise.race 中all是都完成才返回,如果有一个失败就全部终止失败退出,race是任意一个完成就返回

Promise 实现

// 特性:
// - 函数 返回自身,链式调用
// - 构造函数是宏队列,then后是微队列,已敲定后不能再改
// - 1、静态方法:all,allSettled,any,race,resolve,reject
// - all 都是成功返回数组给then,或者有一个失败就都终止且返回错误给catch
// - allSettled 都已敲定状态,有成功有失败,数组返回给then
// - any 任意一个成功就终止,返回给then
// - race 任意一个已敲定就终止, 成功返回给then,失败返回给catch
// - 2、实例方法 then,catch,finally

function Promise(callback) {
    if (!this instanceof Promise) {
        return console.error('must call by new Promise')
    }
    if (typeof callback != 'function') {
        return console.error('need callback function')
    }
    this.status = 'PENDING'
    const resolve = (value) => {
        if (this.status != 'PENDING') return
        this.status = 'FULFILLED'
        this.value = value
        setTimeout(() => {
            this.resolveFn?.(value)
        })
    }
    const reject = (reason) => {
        if (this.status != 'PENDING') return
        this.status = 'REJECTED'
        this.reason = reason
        setTimeout(() => {
            this.rejectFn?.(reason)
        })
    }
    try {
        callback(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
Promise.prototype.then = function (resolve, reject) {
    return new Promise((nextResolve, nextReject) => {
        resolve = resolve || Promise.resolve
        reject = reject || Promise.reject
        nextResolve = nextResolve || Promise.resolve
        nextReject = nextReject || Promise.reject

        const handler = (result) => {
            if (result instanceof Promise) {
                if (result.status == 'FULFILLED') {
                    nextResolve(result.value)
                }
                if (result.status == 'REJECTED') {
                    nextReject(result.reason)
                }
            }
            nextResolve(result)
        }

        this.resolveFn = (value) => {
            const result = resolve(value)
            handler(result)
        }
        this.rejectFn = (reason) => {
            const result = reject(reason)
            handler(result)
        }
    })
}
Promise.prototype.catch = function (reject) {
    return this.then(null, reject)
}
Promise.prototype.finally = function (fn) {
    const newFn = () => fn()
    return this.then(newFn, newFn)
}
Promise.all = function (array) {
    return new Promise((resolve, reject) => {
        const obj = Object.create(null)
        array.forEach((it, index) => {
            it.then(value => {
                obj[index] = value
                if (Object.keys(obj).length == array.length) {
                    resolve(Object.values(obj))
                }
            }, reason => {
                reject(reason)
            })
        })
    })
}
Promise.allSettled = function (array) {
    return new Promise((resolve, reject) => {
        const obj = Object.create(null)
        array.forEach((it, index) => {
            it.then(value => {
                obj[index] = value
                if (Object.keys(obj).length == array.length) {
                    resolve(Object.values(obj))
                }
            }, reason => {
                obj[index] = reason
                if (Object.keys(obj).length == array.length) {
                    reject(Object.values(obj))
                }
            })
        })
    })
}
Promise.any = function (array) {
    return new Promise((resolve) => {
        array.forEach((it) => {
            it.then(value => {
                resolve(value)
            })
        })
    })
}
Promise.race = function (array) {
    return new Promise((resolve, reject) => {
        array.forEach((it, index) => {
            it.then(value => {
                resolve(value)
            }, reason => {
                reject(reason)
            })
        })
    })
}
Promise.resolve = function (value) {
    return new Promise((resolve) => {
        resolve(value)
    })
}
Promise.reject = function (reason) {
    return new Promise((_, reject) => {
        reject(reason)
    })
}