彻底弄懂Promise,并带你手写

867 阅读4分钟

promise

一个 Promise 必然处于以下几种状态之一:

  • 等待(pending) : 初始状态,既没有被兑现,也没有被拒绝。
  • 成功(fulfilled) : 意味着操作成功完成。
  • 失败(rejected) : 意味着操作失败。

我们来看看原生promise

var p0 = new Promise((resolve,reject) => {
})
var p1 = new Promise((resolve,reject) => {
    resolve('成功')
})
var p2 = new Promise((resolve,reject) => {
  reject('失败')
})
var p3 = new Promise((resolve,reject) => {
  resolve('成功')
  reject('失败')
})
 
console.log(p0, p1, p2, p3)

image.png

通过输出结果我们可以总结出

  • 当没有执行resolve或者reject时,PromiseState是pending状态
  • 当执行resolve或者reject时,PromiseState会变成相应的成功或失败状态
  • 状态只能由 Pending --> fulfilled 或者 Pending --> rejected,且一但发生改变便不可二次修改

根据上面的总结,我们来一一实现

class MyPromise {
  constructor(executor) {
    // 初始化值
    this.PromiseState = 'pending'
    this.PromiseResult = null
    // 绑定this
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
    // 执行传进来的函数
    executor(this.resolve,this.reject)
  }

  resolve(value){
  // 只能是Pending --> Fulfilled
    if(this.PromiseState === 'pending') {
      this.PromiseState = 'fulfilled'
      this.PromiseResult = value
    }
  }
  reject(reason){
  // 只能是Pending --> Rejected
    if(this.PromiseState === 'pending') {
      this.PromiseState = 'rejected'
      this.PromiseResult = reason
    }
  }
}


var p01 = new MyPromise((resolve,reject) => {
})
var p11 = new MyPromise((resolve,reject) => {
    resolve('成功')
})
var p21 = new MyPromise((resolve,reject) => {
  reject('失败')
})

image.png

可以发现已经实现了简单的功能

then

看看原生的promise的then

var p1 = new Promise((resolve,reject) => { resolve('成功') })
var p2 = new Promise((resolve,reject) => { reject('失败') })
var p3 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('成功')
    },3000)
})
p1.then(res => {console.log(res)}, err => {console.log(err)}) // 输出:成功
p2.then(res => {console.log(res)}, err => {console.log(err)}) // 输出:失败
p3.then(res => {console.log(res)}, err => {console.log(err)}) // 3s后输出:成功

  • then接收两个回调函数,一个是成功回调,一个是失败回调
  • resolvereject遇到定时器时,等待定时器结束后才执行then

遇到定时器等异步操作的时候的思路

  • 当执行then的时候状态是pending就代表遇到了异步操作,需要保存then里的回调在数组里
  • 当定时器结束的时候再执行存储回调数组
class MyPromise {
  constructor(executor) {
    // 初始化值
    this.PromiseState = 'pending'
    this.PromiseResult = null
    // 异步存储回调 -----新增代码
    this.onFulfilledCallBack = []
    this.onRejectedCallBack = []
    // 绑定this
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
    // 执行传进来的函数
    executor(this.resolve,this.reject)
  }

  resolve(value){
    if(this.PromiseState === 'pending') {
      this.PromiseState = 'fulfilled'
      this.PromiseResult = value
      // -----新增代码
      while(this.onFulfilledCallBack.length) {
        this.onFulfilledCallBack.shift()(value)
      }
    }
  }
  reject(reason){
    if(this.PromiseState === 'pending') {
      this.PromiseState = 'rejected'
      this.PromiseResult = reason
      // -----新增代码
      while(this.onRejectedCallBack.length) {
        this.onRejectedCallBack.shift()(reason)
      }
    }
  }
  then(onFulfilled,onRejected){
    if (this.PromiseState === 'fulfilled'){
      onFulfilled(this.PromiseResult)
    } else if(this.PromiseState === 'rejected'){
      onRejected(this.PromiseResult)
    } else if (this.PromiseState === 'pending') { // -----新增代码
      this.onFulfilledCallBack.push(onFulfilled)
      this.onRejectedCallBack.push(onRejected)
    }
  }
}

数组保存回调的原因:Promise 的 then 方法是可以被多次调用的,如下

var p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve('成功')
  },3000)
})
p1.then(value => { 
  console.log(value + 1)
}) 
p1.then(value => {
  console.log(value + 2)
})

3s后输出成功1,成功2

then的链式调用

怎么让promise可以一直链式的调用then呢? 只需要在then执行返回一个Promise对象就行了

then(onFulfilled,onRejected){
    const promise2 = new MyPromise((resolve,reject) => {
      if (this.PromiseState === 'fulfilled'){
        // 保存回调的结果
        const x = onFulfilled(this.PromiseResult)
        resolvePromise(promise2, x, resolve, reject);
      } else if (this.PromiseState === 'rejected'){
        onRejected(this.PromiseResult)
      } else if (this.PromiseState === 'pending') {
        this.onFulfilledCallBack.push(onFulfilled)
        this.onRejectedCallBack.push(onRejected)
      }
    })
    return promise2;
}

function resolvePromise(promise2, x,resolve,reject) {
  // 如果相等了,说明return的是自己,抛出类型错误并返回 
  if (promise2 === x) { 
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) 
  }
 // 判断x是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    x.then(resolve,reject)
  } else {
    resolve(x)
  }
}

为了加深代码的理解,我们分三种情况理解

1. 当then里执行的不是return的时候,例如

p1.then(res => {
  console.log(res)
}).then(res => {
  console.log(res)
})

当上述代码执行到const x = onFulfilled(this.PromiseResult)这一句的时候相当于立即执行回调,等效于=>const x = ((res) => {console.log(res)})('成功')

2. 当then里return的是普通变量的时候,例如

p1.then(res => {
  return res + '1'
}).then(res => {
  console.log(res)
})

// 执行上述代码简化一下,假设resolve('成功')
then(onFulfilled,onRejected){
    const promise2 = new MyPromise((resolve,reject) => {
      if (this.PromiseState === 'fulfilled'){
        const x = '成功1'
        resolve(x)
      }
    })
    return promise2;
}

返回一个promise,当再调用then的时候就把x的值传递过去实现了链式调用

3. 当then里return的是promise对象的时候,例如

var p1 = new MyPromise((resolve,reject) => {
    resolve('成功')
})
var p2 = new MyPromise((resolve,reject) => {
  resolve('成功')
})

p1.then(res => {
  return new Promise((resolve, reject) =>{
    resolve(res + '-p1.then成功')
  })
}).then(res => {
  console.log(res)
})

p2.then(res => {
  return new Promise((resolve, reject) =>{
    reject(res + '-p1.then失败')
  })
}).then(res => {
  console.log(res)
})

image.png

  • 当返回值是promise对象时,成功则新promise的对象(即promise2)返回的是成功,反之则失败

之所以在resolvePromise方法中,遇到promise对象调用 then 方法,是因为只有then才能知道promise返回的状态是成功还是失败。

4. 当then里return的是promise对象自己的时候,会报错。例如

const promise = new Promise((resolve, reject) => {
    resolve('success')
})
 
// 这个时候将promise定义一个p1,然后返回的时候返回p1这个promise
const p1 = promise.then(value => {
   console.log(1)
   console.log('resolve', value)
   return p1
})

image.png

所以我们需要对这种情况进行处理