DAY8 手写js系列(五)

2022/4/3

# DAY8 手写js系列(五)


[TOC]

# 24. 手写Promise

类的声明

通过new Promise((resolve, reject)=>{})可以创建一个Promise,因此可以写出如下类声明:

class MyPromise {
  constructor(executor) {
    let resolve = () => {}

    let reject = () => {}

    executor(resolve, reject)
  }
}

状态和值的初始化 && resolve / reject 的功能实现

class MyPromise {
  constructor(executor) {
    // 状态和值的初始化
    this.status = 'pending'
    this.value = null
    this.reason = null

    // resolve reject
    let resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled'
        this.value = value
      }
    }

    let reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected'
        this.reason = reason
      }
    }

    // 立即执行
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
}

then方法的实现

  • Promise有一个叫做then的方法,里面有两个参数:onFulfilled,onRejected.

  • 当状态state为fulfilled,则执行onFulfilled,传入this.value。当状态state为rejected,则执行onRejected,传入this.reason

class MyPromise {
  constructor(executor) {...}

  then(onFulfilled , onRejected) {
    if (this.status === 'fulfilled') {
      onFulfilled(this.value)
    } else if (this.status === 'rejected') {
      onRejected(this.reason)
    }
  }
}

异步的实现

调用then方法时如果promise状态未定,要把then的两个参数保存起来,等到状态确定后再拿出来执行。

class MyPromise {
  constructor(executor) {
    // 状态和值的初始化
    ...

    // 存放onFulfilled和onRejected函数调用的数组
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    // resolve reject
    let resolve = (value) => {
      if (this.status === 'pending') {
        ...
        // 状态确定,调用数组里的回调函数
        this.onFulfilledCallbacks.forEach((fn) => fn())
      }
    }

    let reject = (reason) => {
      if (this.status === 'pending') {
        ...
        // 状态确定,调用数组里的回调函数
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }

    // 立即执行
    ...
  }

  then(onFulfilled, onRejected) {
    ...
    else if (this.status === 'pending') {
      this.onFulfilledCallbacks.push(() => {
        	onFulfilled(this.value)
      	})
      this.onRejectedCallbacks.push(() => {
        	onRejected(this.reason)
      	})
    }
  }
}

链式调用

  • then里return 一个promise2
  • promise2的值传递给下一个then
  • onFulfilled和onRejected执行以后返回的值叫做x,需要传入resolvePromise进行判断。
    • x是Promise,则确定它的状态,然后把值作为promise2成功的结果
    • x是普通值,直接作为promise2成功的结果
    • 要比较x和promise2
    • resolvePromise的参数有promise2,x,resolve,reject
    • resolve和reject是promise2的
then(onFulfilled, onRejected) {
    
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === 'fulfilled') {
        let x = onFulfilled(this.value)
        resolvePromise(promise2, x, resolve, reject)
      } else if (this.status === 'rejected') {
        let x = onRejected(this.reason)
        resolvePromise(promise2, x, resolve, reject)
      } else if (this.status === 'pending') {
        this.onFulfilledCallbacks.push(() => {
          let x = onFulfilled(this.value)
          resolvePromise(promise2, x, resolve, reject)
        })
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.reason)
          resolvePromise(promise2, x, resolve, reject)
        })
      }
    })

    return promise2
  }

resolvePromise函数

  • 首先解决 x === promise2循环引用问题
if (x === promise2) {
    return reject(new TypeError('chaining cycyle detected for promise'))
}
  • x不是null 且x是对象或者函数

    • A+规定,声明then = x的then方法
  • x是普通值直接成功

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then
        } catch (err) {
    
        }
    } else {
    	resolve(x)
    }
    
  • 判断then是否为函数

    • 是函数,默认x就是promise,调用then方法并传入成功和失败的回调

      then.call(
      	x,
      	(val) => {
      		resolvePromise(promise2, val, resolve, reject)
      	},
      	(err) => {
      		reject(err)
      	}
      )
      
    • 为了防止成功和失败的回调同时调用,使用一个called变量

      let called = false
      if (typeof then === 'function') {
          then.call(
              x,
              (val) => {
                  if (called) return
                  called = true
                  resolvePromise(promise2, val, resolve, reject)
              },
              (err) => {
                  if (called) return
                  called = true
                  reject(err)
              }
          )
      }else{...}
      
    • 不是函数,x不是promise,直接成功返回x

      try {
              let then = x.then
              
              if(typeof then === 'function'){
                  
              }else{
                  resolve(x)
              }
          } catch (err) {
      
          }
      
    • catch到错误,直接错误

      catch (err) {
      	if (called) return
      	called = true
      	reject(err)
      }
      

解决其他问题

  • onFulfilled / onRejected都是可选的参数,如果它们不是函数,必须被忽略

  • 进行参数校验,失败时即调用onRejected时直接抛出错误,这样才不会让错误信息跑到下一个then去。

    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err }
    
  • PromiseA+定onFulfilled或onRejected不能同步被调用,必须异步调用。我们就用setTimeout解决异步问题,用setTimeout把所有onFulfilled和onRejected执行包装一下

  • 如果onFulfilled或onRejected报错,则直接返回reject(),这一步用try catch包装一下

    let promise2 = new MyPromise((resolve, reject) => {
        if (this.status === 'fulfilled') {
            setTimeout(() => {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (err) {
                    reject(err)
                }
            }, 0)
        } else if (this.status === 'rejected') {
            try {
                let x = onRejected(this.reason)
                resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
                reject(err)
            }
        } else if (this.status === 'pending') {
            this.onFulfilledCallbacks.push(() => {
                try {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (err) {
                    reject(err)
                }
            })
            this.onRejectedCallbacks.push(() => {
                try {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (err) {
                    reject(err)
                }
            })
        }
    })
    

catch方法

  • 调用then方法并将catch接收的参数传入作为onRejected
catch(fn) {
    this.then(null, fn)
  }

resolve和reject方法

// resolve
MyPromise.resolve = function (val) {
  return new MyPromise((resolve, reject) => {
    resolve(val)
  })
}

// reject方法
MyPromise.reject = function (err) {
  return new MyPromise((resolve, reject) => {
    reject(err)
  })
}

race方法

// race方法
MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject)
    }
  })
}

all方法

  • 传入一个promises数组
  • 每一个promise调用then方法,把结果放到结果数组再一起返回
// all方法
MyPromise.all = function (promises) {
  let result = []
  let count = 0
  let processData = function (idx, data) {
    result[idx] = data
    count++
    if (count === promises.length) {
      resolve(result)
    }
  }
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((data) => {
        processData(data)
      }, reject)
    }
  })
}

参考: