JS实现Promise

Promise概念

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。

promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

promise解决的问题

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的.

分析Promise

如果我们想要封装promise就需要考虑以下几个问题

  • 如何让Promise变成一个微任务
  • 如何管理Promise的状态
  • then方法的返回值问题
  • 静态方法: resolve 、reject、all、race

Promise中函数的执行顺序

使用示例:new Promise(fn).then(handler)

执行顺序是:new Promise() -> 执行fn里的代码 -> then()函数添加handler队列 -> 异步resovle() -> 在resolveHandler里接收resolve的结果

手写Promise

/**
 * 注意:promise本身不是异步的,它只是能处理异步
 */
class SimplePromise {
    constructor(fn) {
        if (typeof fn !== 'function') {
            throw new Error('callback is not a function')
        }
        this.fn = fn

        // 初始化状态,pending fulfiled rejected,状态变更后不能再改变
        this.status = 'pending'

        // resovle,reject回调函数队列
        this.resovleQueue = []
        this.rejectQueue = []

        // new Promise()后,里面的代码是立即执行的
        fn(this._resovle.bind(this), this._reject.bind(this))

        return this
    }

    _resovle(value) {
        // 调用完resolve()之后会走到then的resolveHandler函数
        // 这里怎么能访问到resolveHandler呢?所以得提前在调用then方法的时候存起来
        // then链式调用,后面的then会用到前面then的返回值
        // 这里注意变成异步任务,否则resovle执行完了才会执行then函数添加handler,这样就访问不到handler了
        // 正确的顺序应该是 new Promise() -> then函数添加handler到队列 -> resolve() -> 执行handler()
        // setTimeout是宏任务,所以不准确
        setTimeout(() => {
            if (this.status !== 'pending') {
                return
            }
            // 修改状态
            this.status = 'fulfiled'
            let res = value
            while (this.resovleQueue.length) {
                let handler = this.resovleQueue.shift()
                res = handler(res)
            }
        }, 0)
    }

    _reject() {
        if (this.status !== 'pending') {
            return
        }
        this.status = 'rejected'
        setTimeout(() => {
            let res = value
            while (this.rejectQueue.length) {
                let handler = this.rejectQueue.shift()
                res = handler(res)
            }
        }, 0)
    }

    // then的本质是调用then()来接收结果
    // 封装then方法,有resolveHandler,rejectHandler两个参数
    // 注意理解,调用then()方法的时候里面的代码还不会执行,要等resovle()之后then里面的代码才会执行
    then(resolveHandler, rejectHandler) {
        if (resolveHandler) {
            this.resovleQueue.push(resolveHandler)
        }
        if (rejectHandler) {
            this.rejectQueue.push(rejectHandler)
        }

        return this
    }

    // catch本质是reject
    catch(rejectHandler) {
        return this.then(undefined, rejectHandler)
    }

    // Promise.all([p1,p2...]).then([res1,res2...])
    // 入参是迭代器,可以是Array,Map,Set
    // 能使用then,所以Promise.all()会返回一个promise对象
    static all(iterators) {
        let promises = Array.from(iterators) // 转为数组
        // results是从p.then()接收的,注意顺序是对应的
        let results = new Array(promises.length)
        let count = 0
        return new SimplePromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(res => {
                    results[i] = res

                    // 什么时候resovle results呢?
                    // 等所有结果响应的时候,所以需要一个计数器
                    count++
                    if (count === promises.length) {
                        resolve(results)
                    }

                    // 注意return,否则链式调用then获取不到上一个then的结果
                    return res
                }).catch(err => {
                    reject(err)
                })
            }
        })
    }

    // Promise.race([p1,p2...]).then(firstRes)
    static race(iterators) {
        let promises = Array.from(iterators)
        return new SimplePromise((resolve, reject) => {
            for (let p of promises) {
                p.then(res => {
                    // 只要有一个结果响应了就resolve
                    resolve(res)
                }).catch(() => {
                    reject()
                })
            }
        })
    }

    // Promise.resolve(val).then()
    static resovle(val) {
        return new SimplePromise(resovle => {
            resovle(val)
        })
    }

    // Promise.reject(val).then()
    static reject(val) {
        return new Promise((resovle, reject) => {
            reject(val)
        })
    }
}

// let p = new SimplePromise((resolve, reject) => {
//     console.log(1)
//     setTimeout(() => {
//         resolve()
//     }, 1000)
// }).then(res => {
//     console.log(2)
//     return 3
// }).then(res => {
//     console.log(3)
// })

let p0 = SimplePromise.resovle('p0')
let p1 = new SimplePromise((resolve) => {
    setTimeout(() => {
        resolve('p1')
    }, 1000)
})
let p2 = new SimplePromise((resolve) => {
    setTimeout(() => {
        resolve('p2')
    }, 2000)
})
let p3 = new SimplePromise((resolve) => {
    setTimeout(() => {
        resolve('p3')
    }, 5000)
})

// SimplePromise.all([p1, p2, p3]).then(results => {
//     console.log(results);
// })

SimplePromise.race([p3, p2, p1, p0]).then(res => {
    console.log(res)
})