通俗易懂:手写promise

235 阅读2分钟

Promise 本身是同步

真正的异步是你自己在promise内部写的代码

开始实现promise

大概逻辑

  1. 实例化构造函数,构造函数内部调用传入的function,把resolve和reject传给function

  2. 紧接着调用then方法,把then方法上的一些函数储存起来:(成功函数,失败函数),最终then返回了一个promise,所以还要存储一个promise实例, catch 方法

  2.1 如果是链式调用then,则全部调用,把需要的函数全部存储起来

  1. 接着是你调用resolve或者reject。promise内部修改状态pending --> resolveed 表示成功,如果调用reject则状态被修改为 rejected 表示失败,状态是不可逆的,只能改一次,保证resolve和reject函数只被调用一次

  2. 之后在resolve和reject函数内部使用异步处理(这里是为了防止resolve/reject 同步调用时 ,处理then方法劳动成果的代码执行在then方法之前,导致拿不到then方法的劳动成果)

  3. 处理then方法的成功/失败函数,将resolve/reject的结果传入成功函数,并接收返回值,作为下一个then方法的结果传给promise.resolve,reject函数不接收返回值,直接调用当前then方法的失败函数,把错误抛出去

开始实现

先简单实现promise的这几个方法:

resolve reject then catch

先写一个class

  • promise
class MyPromise{
    status = "pending"
    callbacks = {
        success: null,
        fail: null,
        catch: null,
        promise: null
    }
    constructor(){}
    resolve = res => {}
    reject = err => {}
    then = (success, fail) => {}
    catch = fail => {}
}

constructor

调用传入的函数,把resolve/reject传进去

constructor(fn){
    fn(this.resolve, this.reject)
}

then

保存成功失败函数以及promise,并返回promise

then = (success, fail) => {
    if(typeof success === 'function') {
        this.callbacks.success = success
    }
    if (typeof fail === 'function') {
        this.callbacks.fail = fail
    }
    this.callbacks.promise = new MyPromise(() => {})
    return this.callbacks.promise
}

catch

方法比较简单,只保存失败时的函数

catch = fail => {
    if(typeof fail === 'function'){
        this.callbacks.catch = fail
    }
}

resolve

  • 如果开发者在promise中同步调用resolve/reject,则resolve会在初始化.then方法之前执行,故而在执行resolve时,then方法传入的函数还没有被保存,所以同步调用resolve不会执行.then

  • 使用setTimeout把函数代码块变成异步,放入微任务队列,等宏任务执行完成后再去队列里执行微任务,这时已经初始化完成,该有的函数也都被保存到了对应的位置

resolve = res => {
    if(this.status !== 'pending') return
    this.status = 'fulfilled'
    setTimeout(() => {
        let r = undefined
        if(this.callbacks.success) {
            r = this.callbacks.success(res)
        }
        this.callbacks.promise && this.callbacks.promise.resolve(r)
    })
}

reject

错误处理

reject = err => {
    if(this.status !== 'pending') return
    this.status = 'rejected'
    setTimeout(() => {
        const handleReject = cbk => {
            if(typeof cbk.fail){
                cbk.fail(err)
            } else if(cbk.catch) {
                cbk.catch(err)
            } else {
                handleReject(cbk.promise.callbacks)
            }
        }
        handleReject(this.callbacks)
    })
}