一文学会手写Promise

什么是Promise

在开始编码之前,我们先来看一下什么是Promise,话不多说直接上MDN的描述。

看完MDN的描述我们已经知道Promise原来是一个对象,可以表示异步操作的成功或失败。

PromiseA+规范

Promise的实现要符合PromiseA+规范,那我们就先看一下什么是PromiseA+规范。

从规范中我们了解到,promise是一个对象,要包含一个then方法。具体的内容感兴趣的可以去 原文 了解一下。

开始动手了

js 复制代码
// 定义promise状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'


/**
 * 创建MyPromise类
 */
class MyPromise {
    /**
     * 接受一个执行器
     * @param { function } executor 
     */
    constructor(executor){
        // 默认状态为pending
        this.state = PENDING
        this.value = undefined
        executor(this._resolve.bind(this),this._reject.bind(this))
    }

    /**
     * 更改promise状态为成功
     * @param {*} data 
     */
    _resolve(data) {
        this.state = FULFILLED
        this.value = data
    }

    /**
     * 更改状态为失败
     * @param {*} reason 
     */
    _reject(reason) {
        this.state = REJECTED
        this.value = reason
    }
}

我们已经可以通过MyPromise这个类来创建promise实例了,接下来我们实现then方法并完善一下这个类。

js 复制代码
/**
 * 创建MyPromise类
 */
class MyPromise {
    /**
     * 接受一个执行器
     * @param { function } executor 
     */
    constructor(executor){
        // 默认状态为pending
        this.state = PENDING
        this.value = undefined
        this.handlers = []
        executor(this._resolve.bind(this),this._reject.bind(this))
    }


    /**
     * 变更状态的方法
     */
    _changeState(state,value) {
        if(this.state!==PENDING) { return }
        this.state = state
        this.value = value
    }

    /**
     * 往任务队列添加任务
     */

    _pushHandler(executor,state,resolve,reject){
        this.handlers.push({
            executor,state,resolve,reject
        })
    }

    /**
     * 更改promise状态为成功
     * @param {*} data 
     */
    _resolve(data) {
        this._changeState(FULFILLED,data)
    }

    /**
     * 更改状态为失败
     * @param {*} reason 
     */
    _reject(reason) {
        this._changeState(REJECTED, reason)
    }

    /**
     * 符合Promise A+规范的then方法
     */

    then(onFulfilled,onRejected){
        // then方法返回一个promise执行
        return new MyPromise((resolve,reject) => {
            this._pushHandler(onFulfilled,FULFILLED,resolve,reject)
            this._pushHandler(onRejected,REJECTED,resolve,reject)
        })
    }
}

ok,then方法已经添加了,现在已经可以把任务添加到存储任务的数组了,你是不不是以为这就大功告成了。NONONO,我们还少了最重要的一步,任务的执行。说到任务的执行,那就离不了宏任务和微任务的概念了,让我们先来看一下什么是宏任务和微任务吧。

宏任务与微任务

我们学习js听的最多的一句话应该就是js是单线程执行了。那既然是单线程执行遇到比较耗时的任务时怎么处理呢,总不能一直阻塞吧。为了解决这个问题,js划分了同步任务和异步任务。异步任务又分为宏任务和微任务。 常见的任务创建方式:

  • 宏任务: script(整体代码块)、settimeout、setInterval、setImmediate(node环境)
  • 微任务:MutationObserver(浏览器环境)、 process.nextTick(node环境)、queueMicrotask

这里对异步机制介绍的比较粗陋,感兴趣的同学可以移步 js执行机制去了更多。

实现任务执行

我们先来写一个执行微任务的辅助函数和一个判断是否是promise的函数

js 复制代码
/**
 * 接收一个函数放入微队列执行
 * @param { function } callback 
 */
function runMincroTask(callback) {
    if(queueMicrotask) {
        queueMicrotask(callback)
    } else if(process&&process.nextTick){
        process.nextTick(callback)
    } else if(MutationObserver){
        let p = document.createElement('p')
        const ob = new MutationObserver(callback)
        ob.observe({
            childList: true
        })
        p.innerHTML = '1'
    } else {
        setTimeout(callback,0)
    }
}


/**
 * 接受一个任意参数,判断是否为promise
 * @param {*} obj 
 * @returns 
 */
function isPromise(obj) {
    return !!(obj&&typeof obj==='object' && typeof obj.then === 'function' )
}

准备工作搞定了,接下来我们来实现任务执行的方法。

js 复制代码
 /**
     * 任务执行方法
     */
    _runHandler() {
        if(this.state===PENDING) { return }
        // 拿出任务队列的每一项执行
        while(this.handlers[0]) {
            const handle = this.handlers[0]
            this._runOneHandler(handle)
            this.handlers.shift()
        }
    }

    /**
     * 执行任务队列的每一项任务
     */
    _runOneHandler({executor,state,resolve,reject}){
        // 把任务放入微任务队列执行
        runMincroTask(() => {
            if(state!==this.state) { return }
            if(typeof executor !== 'function') {
                this.state === FULFILLED ? resolve(this.value) : reject(this.value)
                return
            }
            try {
                const result = executor(this.value)
                if(isPromise(result)) {
                    result.then(resolve,reject)
                } else {
                    resolve(this.value)
                }
            } catch (error) {
                reject(this.value)
                console.log(error)
            }
        })
    }

呼~,任务执行的方法搞定了。接下来我们在状态改变和then方法中添加调用。

js 复制代码
/**
     * 变更状态的方法
     */
    _changeState(state,value) {
        if(this.state!==PENDING) { return }
        this.state = state
        this.value = value
        // 状态变更调用任务执行
        this._runHandler()
    }
    
    
    then(onFulfilled,onRejected){
        // then方法返回一个promise执行
        return new MyPromise((resolve,reject) => {
            this._pushHandler(onFulfilled,FULFILLED,resolve,reject)
            this._pushHandler(onRejected,REJECTED,resolve,reject)
            this._runHandler()
        })
    }

验证

大功告成,接下来我们来验证是否好使吧。

js 复制代码
const p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 2000)
})
const p1 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject(2)
    }, 1000) 
})

Promise.any([
    p,p1
]).then((first) => {  // 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
});

利用原生Promise 和 MyPromise进行测试,最终结果输出正确。至此一个符合Promise A+规范的 Promise就完成了。

catch方法实现

js 复制代码
/**
     * 处理任务失败的情况
     * @param {*} onRejected 
     * @returns 
     */
    catch(onRejected) {
        return this.then(null ,onRejected)
    }

finally方法实现

js 复制代码
/**
 * 不论成功或失败都会执行的方法
 * @param {*} onSetteld 
 */
finally(onSetteld){
    return this.then(data => {
        onSetteld()
        return data
    }, reason => {
        onSetteld()
        throw reason
    })
}

resolve和reject方法实现

js 复制代码
/**
     * 
     * @param {*} obj 需要转为promise的对象
     */
    static resolve(obj) {
        return new MyPromise((resolve,reject) => {
            if(obj instanceof MyPromise) {
                return obj
            }
            if(isPromise(obj)) {
                obj.then(resolve,reject)
            } else {
                resolve(obj)
            }
        })
    }

    /**
     * 返回一个状态为被拒绝的promise
     * @param {*} reason 失败原因
     * @returns 
     */
    static reject(reason) {
        return new MyPromise((resolve,reject) => {
            reject(reason)
        })
    }

all方法实现

js 复制代码
/**
     * 接受一个具有interator属性的值,
     * @param { interator } arr 
     */
    static all(arr) {
        return new MyPromise((resolve, reject) => {
            try {
                let count = 0
                let fulfilledCount = 0
                let results = []
                for (const item of arr) {
                    let i = count
                    count++
                    MyPromise.resolve(item).then(data => {
                        fulfilledCount++
                        results[i] = data
                        if (count === fulfilledCount) {
                            resolve(results)
                        }
                    }, reject)
                }
                if (count === 0) {
                    resolve([])
                }
            } catch (err) {
                reject(err)
            }

        })
    }

allSettled方法实现

js 复制代码
/**
     * 接受一个包含多个promise的参数
     * 所有promise已决后返回结果
     * @param { interator } arr 
     */
    static allSettled(arr) {
        return new MyPromise((resolve,reject) => {
            let results = []
            for (const p of arr) {
                results.push( MyPromise.resolve(p).then(
                    data=> ({ status: FULFILLED, value:data })),
                    reason => ({ status: REJECTED, reason })
                 )
            }
            return MyPromise.all(results)
        })
    }

race方法实现

js 复制代码
/**
     * 接受一个包含多个promise的参数
     * 返回最先完成的promise结果,不论成功还是失败
     * @param { interator } arr 
     */
    static race(arr) {
        return new MyPromise((resolve,reject) => {
            for (const item of arr) {
                MyPromise.resolve(item).then(resolve,reject)
            }
        })
    }

any方法实现

js 复制代码
/**
 * 接受一个包含多个promise的参数
 * 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;
 * 如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态
 * @param { interator } arr 
 */
static any(arr) {
    return new MyPromise((resolve,reject) => {
        let rejectCount = 0
        let errResults = []
        for (const item of arr) {
            MyPromise.resolve(item).then(resolve,reason => {
                rejectCount++
                errResults[rejectCount] = { reason }
                if(errResults===arr.length) {
                    reject(errResults)
                }
            })
        }
        if(rejectCount===0) {
            resolve([])
        }
    })
}

全部完结啦!!!!!

贴上完整代码

js 复制代码
// 定义promise状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/**
 * 接收一个函数放入微队列执行
 * @param { function } callback 
 */
function runMincroTask(callback) {
    if (queueMicrotask) {
        queueMicrotask(callback)
    } else if (process && process.nextTick) {
        process.nextTick(callback)
    } else if (MutationObserver) {
        let p = document.createElement('p')
        const ob = new MutationObserver(callback)
        ob.observe({
            childList: true
        })
        p.innerHTML = '1'
    } else {
        setTimeout(callback, 0)
    }
}

/**
 * 接受一个任意参数,判断是否为promise
 * @param {*} obj 
 * @returns 
 */
function isPromise(obj) {
    return !!(obj && typeof obj === 'object' && typeof obj.then === 'function')
}

/**
 * 创建MyPromise类
 */
class MyPromise {
    /**
     * 接受一个执行器
     * @param { function } executor 
     */
    constructor(executor) {
        // 默认状态为pending
        this.state = PENDING
        this.value = undefined
        this.handlers = []
        try {
            executor(this._resolve.bind(this), this._reject.bind(this))
        } catch (error) {
            this._reject(error)
            console.log(error)
        }

    }


    /**
     * 变更状态的方法
     */
    _changeState(state, value) {
        if (this.state !== PENDING) { return }
        this.state = state
        this.value = value
        // 状态变更调用任务执行
        this._runHandler()
    }

    /**
     * 往任务队列添加任务
     */

    _pushHandler(executor, state, resolve, reject) {
        this.handlers.push({
            executor, state, resolve, reject
        })
    }

    /**
     * 任务执行方法
     */
    _runHandler() {
        if (this.state === PENDING) { return }
        // 拿出任务队列的每一项执行
        while (this.handlers[0]) {
            const handle = this.handlers[0]
            this._runOneHandler(handle)
            this.handlers.shift()
        }
    }

    /**
     * 执行任务队列的每一项任务
     */
    _runOneHandler({ executor, state, resolve, reject }) {
        // 把任务放入微任务队列执行
        runMincroTask(() => {
            if (state !== this.state) { return }
            if (typeof executor !== 'function') {
                this.state === FULFILLED ? resolve(this.value) : reject(this.value)
                return
            }
            try {
                const result = executor(this.value)
                if (isPromise(result)) {
                    result.then(resolve, reject)
                } else {
                    resolve(this.value)
                }
            } catch (error) {
                reject(this.value)
                console.log(error)
            }
        })
    }

    /**
     * 更改promise状态为成功
     * @param {*} data 
     */
    _resolve(data) {
        this._changeState(FULFILLED, data)
    }

    /**
     * 更改状态为失败
     * @param {*} reason 
     */
    _reject(reason) {
        this._changeState(REJECTED, reason)
    }

    /**
     * 符合Promise A+规范的then方法
     */

    then(onFulfilled, onRejected) {
        // then方法返回一个promise执行
        return new MyPromise((resolve, reject) => {
            this._pushHandler(onFulfilled, FULFILLED, resolve, reject)
            this._pushHandler(onRejected, REJECTED, resolve, reject)
            this._runHandler()
        })
    }

    /**
     * 处理任务失败的情况
     * @param {*} onRejected 
     * @returns 
     */
    catch(onRejected) {
        return this.then(null, onRejected)
    }

    /**
     * 不论成功或失败都会执行的方法
     * @param {*} onSetteld 
     */
    finally(onSetteld) {
        return this.then(data => {
            onSetteld()
            return data
        }, reason => {
            onSetteld()
            throw reason
        })
    }

    /**
     * 
     * @param {*} obj 需要转为promise的对象
     */
    static resolve(obj) {
        return new MyPromise((resolve, reject) => {
            if (obj instanceof MyPromise) {
                return obj
            }
            if (isPromise(obj)) {
                obj.then(resolve, reject)
            } else {
                resolve(obj)
            }
        })
    }

    /**
     * 返回一个状态为被拒绝的promise
     * @param {*} reason 失败原因
     * @returns 
     */
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }

    /**
     * 接受一个具有interator属性的值,
     * @param { interator } arr 
     */
    static all(arr) {
        return new MyPromise((resolve, reject) => {
            try {
                let count = 0
                let fulfilledCount = 0
                let results = []
                for (const item of arr) {
                    let i = count
                    count++
                    MyPromise.resolve(item).then(data => {
                        fulfilledCount++
                        results[i] = data
                        if (count === fulfilledCount) {
                            resolve(results)
                        }
                    }, reject)
                }
                if (count === 0) {
                    resolve([])
                }
            } catch (err) {
                reject(err)
            }

        })
    }


    /**
     * 接受一个包含多个promise的参数
     * 所有promise已决后返回结果
     * @param { interator } arr 
     */
    static allSettled(arr) {
        return new MyPromise((resolve,reject) => {
            let results = []
            for (const p of arr) {
                results.push( MyPromise.resolve(p).then(
                    data=> ({ status: FULFILLED, value:data })),
                    reason => ({ status: REJECTED, reason })
                 )
            }
            return MyPromise.all(results)
        })
    }

    /**
     * 接受一个包含多个promise的参数
     * 返回最先完成的promise结果,不论成功还是失败
     * @param { interator } arr 
     */
    static race(arr) {
        return new MyPromise((resolve,reject) => {
            for (const item of arr) {
                MyPromise.resolve(item).then(resolve,reject)
            }
        })
    }
    /**
     * 接受一个包含多个promise的参数
     * 只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;
     * 如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态
     * @param { interator } arr 
     */
    static any(arr) {
        return new MyPromise((resolve,reject) => {
            let rejectCount = 0
            let errResults = []
            for (const item of arr) {
                MyPromise.resolve(item).then(resolve,reason => {
                    rejectCount++
                    errResults[rejectCount] = { reason }
                    if(errResults===arr.length) {
                        reject(errResults)
                    }
                })
            }
            if(rejectCount===0) {
                resolve([])
            }
        })
    }
}
相关推荐
XiYang-DING10 分钟前
JavaScript
开发语言·javascript·ecmascript
空中海1 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
空中海2 小时前
02 状态、Hooks、副作用与数据流
开发语言·javascript·ecmascript
空中海2 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
杨超凡3 小时前
豆包收费了?我特么自己用“意念”搓了一个!
javascript
threelab4 小时前
Three.js 咖啡杯烟雾效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能
Heo4 小时前
14_React 中的更新队列 updateQueue
前端·javascript·面试
前端 贾公子4 小时前
解决浏览器端 globalThis is not defined 报错
前端·javascript·vue.js
之歆4 小时前
DAY12_CSS3选择器全攻略 + 盒子新特性完全指南(下)
前端·javascript·css3