7、简单手写 Promise

28 阅读3分钟

手写代码的思路为:先从使用开始进行分析。

Promise 的基本使用:

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 2000)
}).then(res => {
  console.log(res) // 'ok'
}, err => {
  console.log(err)
})

根据基本使用,再写出注释:

// 1、Promise 为构造函数
// 2、入参为函数,该函数接受两个参数 resolve, reject,该函数是同步运行的
// 3、调用 resolve,则走向成功
// 4、调用 reject,则走向失败
// 5、.then 表明 new Promise 后会返回对象,并且该 then 接受两个参数
// 6、其他情况,它的状态有三个:pending、fulfilled、rejected

function MyPromise(fn) {
  // 默认状态
  this.state = 'pending'

  // Promise 的值,通过 resolve、reject 传入的
  this.value = ''

  // 保存下 then 方法收到的函数
  this.thenSuccessFun = []
  this.thenFailFunFun = []


  const resolve = (res) => {
    this.state = 'fulfilled'
    this.value = res

    // 当 thenSuccessFun 有长度时,则需要执行里面的方法
    if(this.thenSuccessFun.length) {
      this.thenSuccessFun.forEach(fn => fn(this.value))
    }
  }
  
  const reject = (err) => {
    this.state = 'rejected'
    this.value = err

    // 当 thenFailFunFun 有长度时,则需要执行里面的方法
    if(this.thenFailFunFun.length) {
      this.thenFailFunFun.forEach(fn => fn(this.value))
    }
  }


  // 执行传入的函数
  fn(resolve, reject)

  // then 方法
  this.then = (successFun, failFun) => {
    if(this.state === 'pending') {
      // 调用 then 时,状态为 pending,表面 fn 里面还未调用 resolve/reject
      // 则说明 fn 里面有异步操作,就不能直接调用 successFun/failFun
      // 所以需要先存一下
    	if(typeof successFun === 'function') {
        this.thenSuccessFun.push(successFun)
      }

      if(typeof failFun === 'function') {
        this.thenFailFunFun.push(failFun)
      }
      
    } else if(this.state === 'fulfilled') {
			// 调用 then 时,状态为 fulfilled,表面 fn 里面已调用 resolve
      // 则可以直接调用 successFun
      if(typeof successFun === 'function') {
        successFun(this.value)
      }

    } else {
			// 调用 then 时,状态为 rejected,表面 fn 里面已调用 reject
			// 则可以直接调用 failFun
      if(typeof failFun === 'function') {
        failFun(this.value)
      }
    }
  }
}


// 调试:
new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 2000)
}).then(res => {
  console.log(res) // 'ok'
}, err => {
  console.log(err)
})

// 打印结果:
// ok

上述展示了最基本的使用的实现,但是该实现与源码差的很远,只是让大家入个门,还要考虑各种边界与能力。

其他静态方法的实现原理:

Promise.resolve(anyValue):Promise 成功的返回

Promise.resolve = (value) => {
  // 当传入值为 Promise 时,直接返回
	if(value instanceof Promise) return value
  
  // 返回的还是个 Promise
  return new Promise((resolve) => {
  	resolve(value)
  })
}

Promise.reject(anyValue):Promise 失败的返回

Promise.reject = (value) => {
  // 当传入值为 Promise 时,直接返回
	if(value instanceof Promise) return value
  
  // 返回的还是个 Promise
  return new Promise((resolve, reject) => {
  	reject(value)
  })
}

Promise.all(Array<Promise>):Promise 当所有的 Promise 成功时,则返回成功值的数组,否则返回第一个失败的值

Promise.all = (PromiseArray) => {
  // 返回的还是个 Promise
  return new Promise((resolve, reject) => {
   const result = [] // 存储成功值的数组
  
   // 循环执行 promise,当错误时,直接 reject,当成功时,存储值
   PromiseArray.forEach((promise, index) => {
     // 最好改为 for 循环,这样可以中断
     
     Promise.resolve(promise).then(res => {
       result.push(res)

       // 循环执行完毕时,则 resolve(result) 
       if(result.length - 1 === index) resolve(result) 
     }).catch(err => {
       reject(err)
     })
   })
  })
}

Promise.allSettled(Array<Promise>):Promise 当所有的 Promise 执行完时,返回所有结果的数组

Promise.allSettled = (PromiseArray) => {
  // 返回的还是个 Promise
  return new Promise((resolve, reject) => {
   const result = [] // 存储所有值的数组
  
   // 循环执行 promise
   PromiseArray.forEach((promise, index) => {     
     Promise.resolve(promise).then(res => {
       result.push({state: 'success', value: res})

       // 循环执行完毕时,则 resolve(result) 
       if(result.length - 1 === index) resolve(result) 
     }).catch(err => {
       result.push({state: 'fali', value: err})

       // 循环执行完毕时,则 resolve(result) 
       if(result.length - 1 === index) resolve(result) 
     })
   })
  })
}

Promise.race(Array<Promise>):Promise 返回最先执行成功的值

Promise.allSettled = (PromiseArray) => {
  // 返回的还是个 Promise
  return new Promise((resolve, reject) => {
   // 循环执行 promise
   PromiseArray.forEach((promise, index) => {     
     Promise.resolve(promise).then(resolve).catch(reject)
   })
  })
}