promise实现原理解析

一、异步任务的处理

在 ES6 出来之后,有很多有关于 Promise 的讲解,文件也有很多经典的书籍讲解 Promise

虽然等你学会 Promise 之后,会觉得 Promise 不过如此,但是第一次接触后 Promsie 还是很不好理解

那么我们就以一个实际的例子作为切入口

  • 调用一个函数,这个函数中返回网络请求(以定时器模拟)
  • 发送网络请求成功,就告知调用者成功,并返回数据
  • 发送网络请求失败,就告知调用者失败,并返回失败信息
function request(cb) {
  // 模拟网络请求
  let flag = Math.random() <= 0.5 ? true : false
  setTimeout(() => {
    cb(flag, flag ? '成功的数据' : '失败的信息')
  }, 1000)
}

console.log('发起请求')

request((status, msg) => {
  console.log(status, msg)
})

request((s1, m1) => {
    request((s2, m2) => {
        request((s3, m3) => {
            // 出现了回调地狱
        }
    })
})

使用这种回调函数存在的弊端:

  • 如果是自己封装的请求方法,必须要设计好
  • 如果使用他人库,必须要通过查看文档或源码才能直到如何使用这个函数
  • 极容易出现回调地狱

所以,需要一种异步操作的规范,而 Promise 生来就是为了解决异步操作的问题

二、Promise

1. 什么是promise

Promise是一个类,可以翻译为承诺、期约

当通过new创建Promise实例时,需要传入一个回调函数,我们称之为executor

  • 这个回调函数会被立刻执行,并传入两个回调参数resolvereject
  • 当调用resolve回调函数时,会执行 Promise 对象的then方法传入的回调
  • 当调用reject回调函数时,会执行 Promise 对象的catch方法传入的回调

Promise是一个状态机,分为 3 种状态:

  • pending:待定状态,执行了 executor 后,处于该状态
  • fulfilled:兑现状态,调用resolve()后,Promise 的状态更改为 fullfilled,且无法再次更改
  • rejected:拒绝状态,调用reject()后,Promise 的状态更改为 rejected,且无法再次更改
function request() {
  const flag = Math.random() <= 0.5 ? true : false
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (flag) {
        resolve('成功的消息')
        return
      }
      reject('失败的消息')
    }, 2000)
  })
}

console.log('请求开始')
request()
  .then(msg => console.log(msg), err => console.log(err))

2. resolve 的参数

resolve 传入的参数情况:

  • 如果传入的普通的值或对象,那么就会被传递到 then 的参数中
  • 如果传入的是一个 Promise,那么当前的 Promise 的状态将会由传入的 Promise 来决定
const newPromise = new Promise((resolve, reject) => {
  resolve('success')
})

new Promise((resolve, reject) => {
  // 当前 Promise 的状态由传入的 Promise 去决定
  resolve(newPromise)
})
  .then(res => {
    console.log('res', res)
  })
  .catch(err => {
    console.log('err', err)
  })
  • 如果传入的是一个对象,且该对象实现了 then 方法(thenable),也会执行该 then 方法,并且由该 then 方法决定后续的状态
new Promise((resolve, reject) => {
  // 如果 resolve 传入的是对象,且该对象实现了 then 方法
  // 则该 Promise 的状态由 then 方法决定
  resolve({
    then(resolve, reject) {
      reject('error')
    },
  })
})
  .then(res => {
    console.log('res', res)
  })
  .catch(err => {
    console.log('err', err)
  })

3. Promise 的实例方法

1. then方法

通过then方法可以对 Promise 中的resolve进行处理。then方法的返回值是一个 Promise 实例

new Promise(resolve => {
  resolve('你好')
}).then(res => console.log(res)) // 会打印你好

多次调用 then 方法

同一个 Promise 实例可以调用多个then方法,当 Promise 中resolve被回调时,所有 then 方法传入的回调函数都会被调用

const promise = new Promise(resolve => {
  resolve('你好')
})

// 同时调用
promise.then(res => console.log(res))
promise.then(res => console.log(res))
promise.then(res => console.log(res))

then 方法传入的回调函数可以有返回值

如果返回的是普通值,那么这个普通值将作为一个新的 Promise 的resolve的值

const promise = new Promise(resolve => {
  resolve('你好')
})

promise.then(() => 'then').then(res => console.log(res)) // 打印 then
// promise.then(() => 'then') 相当于
promise.then(() => {
  return new Promise(resolve => {
    resolve('then')
  })
})

如果返回的是 Promise,那么就可以再次调用then方法

const promise = new Promise(resolve => {
  resolve('你好')
})
promise
  .then(() => {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve('success')
      }, 2000)
    })
  })
  .then(msg => {
    // 2 秒后打印 success
    console.log(msg)
  })

如果返回的是一个对象,并且该对象实现了 thenable,该 then 函数有两个参数resolvereject,则 resolve 的将会传递给下一个 Promise。

const promise = new Promise(resolve => {
  resolve('你好')
})
promise
  .then(() => {
    return {
      then(resolve) {
        return resolve('success')
      },
    }
  })
  .then(msg => {
    // 打印 success
    console.log(msg)
  })
2. catch 方法

除了then方法的第二个参数来捕获reject错误之外,还可以通过catch方法,catch 返回一个 Promise

const promise = new Promise((resolve, reject) => {
  reject('error')
})

promise.then(undefined, err => {
  // 打印 error
  console.log(err)
})

// 但是这种写法不太符合`promise/a+`规范
promise.catch(err => {
  // 打印 error
  console.log(err)
})

// 下面是符合`promise/a+`规范的写法
promise
  .then(() => {})
  .catch(err => {
    console.log(err)
  })
// 已知 then 方法也可以返回一个 promise,因此在 then 后面追加 catch,以此来捕获 rejected 的情况,更加具有语义化

catch方法也是可以多次调用的,只要 Promise 实例的状态为 rejected,那么就会调用catch方法

const promise = new Promise((resolve, reject) => {
  reject('error')
})

// 这两个 catch 都会调用
promise.catch(err => {
  console.log(err)
})
promise.catch(err => {
  console.log(err)
})

catch 方法的返回值

catch 方法也会返回一个Promise实例,返回值的情况:

  • 普通值,将作为resolve的参数
3. finally 方法

finally 是 ES9(ES2018) 新增的一个特性,无论一个Promise实例是fulfilledrejectedfinally都会执行。

finally 不接收参数。

const promise = new Promise((resolve, reject) => {
  reject('error')
})

promise
  .then(res => {
    console.log('res:', res)
  })
  .catch(err => {
    console.log(('err', err))
  })
  .finally(() => {
    console.log('finally code execute')
  })

4. Promise 的类方法

1. resolve 方法

如果我们想要将一个现成的数据转换为一个 Promise 实例,那么你可以这么做:

const foo = {
  name: 'alex',
}

function bar(obj) {
  return new Promise(resolve => {
    resolve(obj)
  })
}

bar(foo).then(res => {
  console.log(res)
})

还可以直接类方法resolve(),使用Promise.resolve()相当于new Promise(resolve => { resolve() })

function bar(obj) {
  return Promise.resolve(obj)
}

resolve 参数形态:

  • 参数本身是 Promise
  • 参数是原始值/对象
  • 参数是一个 thenable
2. reject 方法

Promise.resolve()方法逻辑基本相同,只不过Promise.reject()相当于创建一个 Promise 实例,并且 rejected 了

Promise.reject('error').catch(error => {
  console.log('error', error)
})

注意:与Promise.resolve()不同的是,Promise.reject()无论传递什么参数都会原样输出

Promise.reject(
  new Promise(resolve => {
    resolve('hello')
  })
).catch(err => {
  // 原样打印 Promise 实例
  console.log('err', err)
})
3. all 方法

Promise.all()接收一个Promise[],返回一个Promise实例,当所有的 Promise 执行完毕并且都是fulfilled时,该实例的状态才会变为fulfilled,只要队列中有一个实例的状态是rejected,那么该实例的状态也会变为rejected

如果 Promise 队列中所有的实例状态都是fulfilled,那么Promise.all()返回的实例的状态就会变为fulfilled,并且resolve()的参数是一个数组,按照顺序放置队列中每个 Promise 成功后的结果

let i = 0
function genPromise() {
  return new Promise(resolve => {
    resolve(`success${(i = i + 1)}`)
  })
}

const promiseArr = [genPromise(), genPromise(), genPromise()]

Promise.all(promiseArr).then(res => {
  // [ 'success1', 'success2', 'success3' ]
  console.log('res', res)
})

如果队列中 Promise 实例有一个是rejected,那么Promise.all()返回的实例就会变为rejected状态,并且reject()参数是队列中第一个rejected的返回值

const promiseArr = [
  genPromise(),
  new Promise((resolve, reject) => {
    reject('error1')
  }),
  new Promise((resolve, reject) => {
    reject('error2')
  }),
]

Promise.all(promiseArr)
  .then(res => {})
  .catch(err => {
    // error 1
    console.log(err)
  })

4. allSettled 方法

all方法是有缺陷的,如果在 Promise 队列中有一个状态是 rejected,那么我们就无法获取到其他 fullfilled 以及 pending 的 Promise 实例了。

针对这一情况,在 ES11(ES2020) 中新增了一个 API,Promise.allSettled()

  • 该方法返回的 Promise 实例,会在所有 Promise 实例执行完毕后,状态方可变为fulfilled,并且只会是fulfilled
  • 无论队列中的Promise 实例的状态如何,都能获取到结果
  • 打印的结果,会包含状态与值/原因
const promiseArr = [
  new Promise((resolve, reject) => {
    resolve('success1')
  }),
  new Promise((resolve, reject) => {
    reject('error')
  }),
  new Promise((resolve, reject) => {
    resolve('success2')
  }),
]

Promise.allSettled(promiseArr).then(res => {
  // res [
  //   { status: 'fulfilled', value: 'success1' },
  //   { status: 'rejected', reason: 'error' },
  //   { status: 'fulfilled', value: 'success2' }
  // ]
  console.log('res', res)
})

5. race 方法

Promise.race()同样接收一个 Promise 队列,返回一个 Promise 实例。该方法会对队列任务完成情况进行监听,如果某一个任务最先完成fulfilled/rejected,那么返回的实例的状态也会变成对应的fulfilled/rejected,同时获取到最先完成的结果

const promiseArr = [
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success1')
    }, 1000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('error')
    }, 2000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success2')
    }, 3000)
  }),
]

Promise.race(promiseArr)
  .then(res => {
    console.log('res', res)
  })
  .catch(err => {
    console.log('err', err)
  })
// 最终打印 res success1
// 如果第二个任务最先完成,那么就会打印 err error

6. any 方法

Promise.any()是 ES12 新增的特性,和Promise.race()类似,区别在于:

  • any 方法会等待一个fulfilled状态,才会决定返回 Promise 实例的状态
  • 如果队列中所有的实例都是rejected状态,那也需要等到所有执行完毕后才会决定返回的 Promise 实例的状态
const promiseArr = [
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success1')
    }, 2200)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('error')
    }, 2000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success2')
    }, 3000)
  }),
]

Promise.any(promiseArr)
  .then(res => {
    console.log('res', res)
  })
  .catch(err => {
    console.log('err', err)
  })
// 遇到第一个 fulfilled,就会转变返回的 Promise 实例的状态
// 如果所有的都是 rejected,那么只有所有执行完毕后,返回的 Promise 实例才会转变
// 并且会抛出一个错误:[AggregateError: All promises were rejected]

简单理解来说,Promise.any()会等待第一个fulfilled的 Promise ,如果队列中没有fulfilled,那么就会返回一个错误

猜你喜欢

转载自blog.csdn.net/weixin_53156345/article/details/130703934