Promise深入理解

        本文由浅入深从promise的基本功能开始分析,到最终实现一个可以通过Promise/A+规范872个测试用例的Promise。

 初探 promise
export const getData = () => post('xxxx/xxx');
getData().then(res => {
    // vue 
    this.dataList = res.data;
    // react 
    setData(res.data)
}, err => {
    
})

function post(url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({data: [1,2,3]})
        }, 1000)
    })
} 

从以上调用中可以发现以下规则:

  • Promise 是一个构造函数;

  • Promise 接受一个函数作为参数,这个函数的参数,是两个函数(resolve, reject)

  • Promise 返回一个对象,这个对象包含一个 then 函数,这个then 函数,接收两个参数,这两个参数,也都是函数。

  • Promisestatus

    • pending

      • 初始的状态,可以改变

      • 一个 Promiseresolve 或者 reject 之前,都处于这个状态

      • 我们可以通过调用 resolve 或者 reject 方法,让这个 Promise 变成 fulfilled 或者 rejected 的状态。

    • fulfilled

      • 不可变状态

      • resolve 之后,变成这个状态,拥有一个 value

    • rejected

      • 不可变状态

      • reject 之后,变成这个状态,拥有一个 reason

  • then 函数

    • 参数

      • onFulfilled, onRejected 必须是函数类型,如果不是,应该被忽略;

    • onFulfilledonRejected 的特性

      • promise 变成 fulfilled / rejected 状态的时候,应该调用 onFulfilled / onRejected

      • promise 变成 fulfilled / rejected 状态之前,不应该被调用

      • 只能调用一次。

实现如下:


function LPromise(execute) {
    this.status = "pending";
    this.value = null;
    this.reason = null;
    
    const resolve = (value) => {
        if(this.status === "pending") {
            this.value = value;
            this.status = "fulfilled";
        }
    }
    
    const reject = (reason) => {
        if(this.status === "pending") {
            this.reason = reason;
            this.status = "rejected";
        }
    }
    
    try {
        execute(resolve, reject);
    } catch (error) {
        reject(error)
    }
}

LPromise.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
    onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
    
    if(this.status === "fulfilled") {
        onFulfilled(this.value);
    }
    
    if(this.status === "rejected") {
        onRejected(this.reason);
    }
}
Promise进阶

但是此时并没有实现异步功能,对于以下代码then中并没有打印出什么:

new LPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello luyi');
    }, 1000)
}).then(res => {
    console.log(res)
})

这是因为我们在resolve执行的时候,then函数已经执行过了。所以我们如果要实现异步执行,就需要在等执行resolve或者reject的时候去执行then。也就是resolve / reject 执行了之后,再执行 onfulfilled 和 onjected。而且根据Promise/A+规范:2.2.4.onFulfilled 和 onRejected 只有在完成环境堆栈仅包含平台代码时才可被调用,也就是onFulfilled和onJected需要在当前同步任务执行完后执行,所以我们用queueMicrotask方法执行,这也是为什么不管execute中是否有异步,then中的回调函数总会在同步代码执行完后的原因:


function LPromise(execute) {
    this.status = "pending";
    this.value = null;
    this.reason = null;
    // 数组是因为then方法可以多次注册,多次调用
    this.onFulfilledArray = [];
    this.onRejectedArray = [];
    
    const resolve = (value) => {
        if(this.status === "pending") {
            this.value = value;
            this.status = "fulfilled";
            this.onFulfilledArray.forEach(func => func(value))
        }
    }
    
    const reject = (reason) => {
       if(this.status === "pending") {
           this.reason = reason;
           this.status = "rejected";
           this.onRejectedArray.forEach(func => func(reason))
       }
    }
    
    try {
        execute(resolve, reject);
    } catch (error) {
        reject(error)
    }
}

LPromise.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
    onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
    
    if(this.status === "fulfilled") {
        queueMicrotask(() => {
            onFulfilled(this.value);
        })
    }
    
    if(this.status === "rejected") {
        queueMicrotask(() => {
            onRejected(this.reason);
        })
    }

    if(this.status === "pending") {
        this.onFulfilledArray.push(onFulfilled);
        this.onRejectedArray.push(onRejected);
    }
}

此时我们解决了异步调用的问题,可以发现使用的是发布订阅模式,但对于如下代码发现并不如预期结果:

new LPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello ln');
        console.log("settimeout")
    }, 1000)
}).then(res => {   
    console.log(res);   // hello ln 
    return res + "ln"
}).then(res => {  
    console.log(res)    // hello ln ln
 }) 

所以我们还要实现promise的链式调用功能。

Promise的链式调用
  • then 方法,应该返回一个 Promise

      promise2 = promise1.then(onFulfilled, onRejected)

    • onFulfilled / onRejected 的执行结果,为 x, 调用 resolvePromise

    • 如果 onFulfilled / onRejected 执行时抛出异常,我们 promise2 需要被 reject

    • 如果 onFulfilled / onRejected 不是一个函数,promise2 以 promise1 的 value 或者 reason 触发 fulfilled 和 rejected

promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)。


function LPromise(execute) {
    this.status = "pending";
    this.value = null;
    this.reason = null;

    this.onFulfilledArray = [];
    this.onRejectedArray = [];
    
    const resolve = (value) => {
       if(this.status === "pending") {
            this.value = value;
            this.status = "fulfilled";
            this.onFulfilledArray.forEach(func => func(value))
       }
    }
    
    const reject = (reason) => {
       if(this.status === "pending") {
           this.reason = reason
           this.status = "rejected";
           this.onRejectedArray.forEach(func => func(reason))
       }
    }
    // try catch 
    execute(resolve, reject);
}

LPromise.prototype.then = function(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
    onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
    
    let promise2;

    if(this.status === "fulfilled") {
        return promise2 = new LPromise((resolve, reject) => {
            queueMicrotask(() => {
                try {
                    // promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
                    let result = onFulfilled(this.value);
                    resolve(result);
                } catch(e) {
                    reject(e)
                }
            })
        })
        
    }
    
    if(this.status === "rejected") {
        return promise2 = new LPromise((resolve, reject) => {
            queueMicrotask(() => {
                try {
                    // promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
                    let result = onRejected(this.reason);
                    resolve(result);
                } catch(e) {
                    reject(e)
                }
            })
        })
    }

    if(this.status === "pending") {
        return promise2 = new LPromise((resolve, reject) => {
            this.onFulfilledArray.push(() => {
                try {
                    let result = onFulfilled(this.value);
                    resolve(result);
                } catch(e) {
                    reject(e)
                }
            });
            this.onRejectedArray.push(() => {
                try {
                    // promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
                    let result = onRejected(this.reason);
                    resolve(result);
                } catch(e) {
                    reject(e)
                }
            });
        })
        
    }
}

此时已经解决了promise最重要的异步和链式调用的问题,但还不够,根据规范在then中返回一个值时,需要运行promise的解决过程:

resolvePromise规范

/*

* @param x then中返回的值

* @param promise2 返回的promise

* @param resolve,rject 返回的promise的resolve,reject方法

/*

resolvePromise(promise2, x, resolve, reject)

规则

  • 如果 promise2 和 x 相等,那么 reject error;

  • 如果 x 是一个 promise

    • 如果 x 是一个pending 状态,那么 promise2 必须要再 pending, 直到 x 变成 fulfilled / rejected

    • 如果 x 被 fulfilled, fulfill promise with the same value

    • 如果 x 被 rejected, reject promise with the same reason

  • 如果 x 是一个 object 或者 function

    • Let thenable = x.then

    • 如果 x.then 这一步出错,那么 reject promise with e as the reason

    • 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromiseFn)

      • resolvePromiseFn 的入参是y, 执行 resolvePromise(promise2, y, resolve, reject)

      • rejectPromiseFn 的入参是 r, reject promise with r

      • 如果 resolvePromiseFn 和 rejectPromiseFn 都调用了,那么第一个调用优先,后面的忽略

      • 如果调用then 抛出异常

        • 如果 resolvePromise 或 rejectPromise 已经被调用,可以忽略

      • 如果 then 不是一个 function, fulfill promise with x

以下是完整代码:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function LPromise(executor) {
  this.status = PENDING
  this.value = null
  this.reason = null
  this.onFulfilledCallbacks = []
  this.onRejectedCallbacks = []

  const resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      this.onFulfilledCallbacks.forEach(fun => fun(value))
    }
  }

  const reject = (value) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = value
      this.onRejectedCallbacks.forEach(fun => fun(value))
    }
  }

  try {
    executor(resolve, reject)
  } catch (error) {
    reject(error)
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    reject(new TypeError('error due to circular reference'))
  }

  let consumed = false
  let thenable

  let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && target !== null

  // promise或者类似promise结构的对象都会走此
  if (isComplexResult(x)) {
    try {
      thenable = x.then
      if (typeof thenable === 'function') {

        thenable.call(
            x,
            function (value) {
              if (consumed) {
                return
              }
              consumed = true
              return resolvePromise(promise, value, resolve, reject)
            },
            function (error) {
              if (consumed) {
                return
              }
              consumed = true
              return reject(error)
            }
          )
      } else {
        resolve(x)
      }
    } catch (error) {
      if (consumed) {
        return
      }
      reject(error)
    }
  } else {
    resolve(x)
  }
}

LPromise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
  onRejected = typeof onRejected === 'function' ? onRejected : (error) => { throw error }

  let promise2;

  if (this.status === FULFILLED) {
    return promise2 = new LPromise((resolve, reject) => {
      queueMicrotask(() => {
        try {
          const x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    })
  }

  if (this.status === REJECTED) {
    return promise2 = new LPromise((resolve, reject) => {
      queueMicrotask(() => {
        try {
          const x = onRejected(this.reason)
          resolvePromise(promise2, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    })
  }

  if (this.status === PENDING) {
    return promise2 = new LPromise((resolve, reject) => {
      this.onFulfilledCallbacks.push(() => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      })
      this.onRejectedCallbacks.push(() => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      })
    })
  }
}

最后用官方测试用例跑一下,全部通过:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值