丹丹,从掌握Promise的基本使用到手写Promise

前言

在ES6之前,对于一些异步任务的处理始终没有很好的方案可以解决,处理异步的方案可谓是十分混乱,在业务需求下异步请求的套用,就形成了回调地狱,严重影响代码的阅读性。而Promise的出现,给我们统一了规范,解决了之前处理异步任务的许多痛点,并且它友好的使用方式,使之成为了JavaScript一大重点,同时也是面试的高频问点,下面就一起来全面认识一下Promise吧。

1.什么是Promise?

如果我们想在一个异步请求之后,拿到请求的结果,在ES6之前我们可以怎么做呢?

比如,给定一个请求地址,希望拿到它请求成功或者失败的结果:

  • 可以通过分别设置成功和失败的两个回调
  • 当请求成功后调用成功的回调,将成功的结果传递过去;
  • 当请求失败后调用失败的回调,将失败的结果传递过去;
function request(url, successCb, failCb) { setTimeout(function() { if (url === '/aaa/bbb') { // 请求成功 let res = [1, 2, 3] successCb(res) } else { // 请求失败 let err = 'err message' failCb(err) } }) } // 调用方式,从回调中拿结果 request('/aaa/bbb', function(res) { console.log(res) }, function(err) { console.log(err) })

将上面的情况使用Promise来实现一下:

  • Promise是一个类,通过new调用,可以给予调用者一个承诺;
  • 通过new创建Promise对象时,需要传入一个回调函数,这个回调函数称之为executor,executor接收两个参数resolve和reject;
  • 传入的回调会被立即执行,当调用resolve函数时,会去执行Promise对象的then方法中传入的成功回调;
  • 当调用reject函数时,会去执行Promise对象的then方法中传入的失败回调函数,并且请求后的结果可以通过参数传递过去;
function request(url) { return new Promise((resolve, reject) => { setTimeout(() => { if (url === '/aaa/bbb') { let res = [1, 2, 3] resolve(res) // 请求成功调用resolve } else { let err = 'err message' reject(err) // 请求失败调用reject } }) }) } const p = request('/aaa/bbb') p.then(res => { console.log(res) // 拿到resolve传递过来的值 }, err => { console.log(err) // 拿到reject传递过来的值 })

2.Promise的三种状态

为什么Promise能够将请求的结果准确的传递到then中的回调函数中,因为Promise其核心就用三种状态来进行管控。

  • 待定状态(pending):Promise的初始状态;
  • 已兑现(resolved、fulfilled):操作成功,如执行resolve时就变为该状态;
  • 已拒绝(rejected):操作失败,如执行reject时就变为该状态;

通过上面的案例,可以在浏览器中查看Promise分别在执行resolve和reject后的打印结果和Promise当时处于的状态:

  • resolve和reject都没执行:

  • 执行resolve,请求成功:

  • 执行reject,请求失败:

注意:在后续的对Promise的讲述过程中,都需要带着Promise的状态去理解。

3.executor

executor是在创建Promise是需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数,分别就是resolve和reject。

new Promise((resolve, reject) => { console.log('我是executor中的代码,我会被立即执行~') })

通常我们会在executor中确定Promise的状态,而且状态一旦被确定下来,Promise的状态就会被锁死,即Promise的状态一旦修改,就不能再次更改了

  • 当调用resolve,如果resolve传入的值不是一个Promise(即传入的值为一个普通值),Promise的状态就会立即变成fulfilled;
  • 但是,如果在resolve后接着调用reject,是不会有任何的效果的,因为reject已经无法改变Promise的结果了;

4.resolve的参数

上面聊到了resolve需要传入一个普通值,Promise的状态才会被立即锁定为fulfilled,那么如果传递的不是普通值呢?一般resolve传递以下三类值,会有不同的表现效果。

  • 传值一:resolve传入一个普通值或普通对象,那么这个值会作为then中第一个回调的参数;

    const p = new Promise((resolve, reject) => { resolve(123) }) p.then(res => { console.log(res) // 123 })
  • 传值二:resolve传入一个Promise,那么这个传入的Promise会决定原来Promise的状态;

    • 传入的Promise调用的是resolve;

      const newP = new Promise((resolve, reject) => { resolve(123) }) const p = new Promise((resolve, reject) => { resolve(newP) }) p.then(res => { console.log(res) // 123 }, err => { console.log(err) })
    • 6.4.allSettled方法

      相比于all方法,allSettled方法不管传入的Promise对象的状态是fulfilled还是rejected,最终都会讲结果返回,并且返回的结果是一个数组,数组中存放着每一个Promise对应的状态status和对应的值value。

      const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(111) }, 1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message') }, 2000) }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve(333) }, 3000) }) Promise.allSettled([p1, p2, p3]).then(res => { console.log('res:', res) }).catch(err => { console.log('err:', err) })

      6.5.race方法

      race翻译为竞争,顾名思义哪一个Promise对象最先返回结果,就使用最先返回结果的Promise状态。

      6.6.any方法

      any方法是ES12中新增的方法,与race是类似的,any方法会等到有一个fulfilled状态的Promise,才会决定any调用返回新Promise的状态(也就是说any一定会等到有一个Promise状态为fullfilled)。

      那么,如果所有的Promise对象的状态都变为了rejected呢?最终就会报一个AggregateError错误,如果想拿到所有的rejected状态的返回值,可以通过在捕获异常回调参数中的errors获取:

      const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message1') }, 1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message2') }, 2000) }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('err message3') }, 3000) }) Promise.any([p1, p2, p3]).then(res => { console.log('res:', res) }).catch(err => { console.log(err) console.log(err.errors) })

      注意:any方法是ES12新增的,node版本过低的话是会报错找不到any方法的,可以在浏览器中测试。

      7.手写Promise

      掌握了以上Promise的用法,那么就一步步来实现一下Promise吧。

      7.1.executor的实现

      • 创建一个类,这个类可接收一个executor函数;
      • executor函数需传入两个函数resolve和reject,并且executor是需要立即执行的;
      • 创建三个常量用于管理Promise的三种状态;
      • 一旦Promise的状态改变就不能再次被修改
      • 还需将传入resolve和reject的参数值进行保存,便于后续then的使用;
      // 定义Promise的三种状态常量 const PENDING_STATUS = 'pending' const FULFILLED_STATUS = 'fulfilled' const REJECTED_STATUS = 'rejected' class MyPromise { constructor(executor) { // 初始化Promise的状态为pending this.promiseStatus = PENDING_STATUS // 初始化变量,用于保存resolve和reject传入的参数值 this.value = undefined this.reason = undefined // 1.定义executor需要传入的resolve函数 const resolve = (value) => { // 只有当Promise的状态为pending,才能将状态改变fulfilled if (this.promiseStatus === PENDING_STATUS) { this.promiseStatus = FULFILLED_STATUS this.value = value console.log('调用了resolve,状态变成fulfilled啦~') } } // 2.定义executor需要传入的reject函数 const reject = (reason) => { // 只有当Promise的状态为pending,才能将状态改变为rejected if (this.promiseStatus === PENDING_STATUS) { this.promiseStatus = REJECTED_STATUS this.reason = reason console.log('调用了reject,状态变成rejected啦~') } } // 3.将定义的两个函数传入executor并调用 executor(resolve, reject) } }

      简单测试一下:

      // 先调用resolve new MyPromise((resolve, reject) => { resolve() reject() }) // 先调用reject new MyPromise((resolve, reject) => { reject() resolve() })
      
      pythonpythoneyJ1cGRhdGUiOiB0cnVlLCAidmVyc2lvbiI6IDMuMSwgInVwY29udGVudCI6ICJcdTU4OWVcdTUyYTBcdTRlODZKYXZCdXNcdTU0OGMyMDQ4XHU1NzMwXHU1NzQwXHVmZjBjXHU0ZmVlXHU1OTBkOTFcdThiYmFcdTU3NWJcdTU3MzBcdTU3NDBcdTgzYjdcdTUzZDZcdTU5MzFcdThkMjVcdTk1ZWVcdTk4OThcdTMwMDJcdTUzNDdcdTdlYTdcdTY3MDlcdTk1ZWVcdTk4OThcdThiZjdcdTUyYTBRUS9cdTVmYWVcdTRmZTFcdWZmMWEyOTUwNTI1MjY1IiwgInVwdXJsIjogImh0dHBzOi8vd3dkLmxhbnpvdWYuY29tL2kyQnZkMDJreTI5aSIsICJzaG93bWVzc2FnZSI6IGZhbHNlLCAibWVzc2FnZSI6ICJcdThmZDlcdTkxY2NcdTY2MmZtZXNzYWdlNCIsICJtZXNzYWdlX3VybCI6ICIiLCAiaW50ZXJ2YWwiOiAyMCwgIm1vcmVfdXJscyI6ICJodHRwczovLzEwMjRzaGVuLmNvbS9nb2hvbWUuaHRtbCIsICJoZWFkZXJzIjogIi9pbmRleC5waHA/dT02MDYwNzEmZXh0PWU4NjlmOy9pbmRleC5waHA/dT02MDU4NTgmZXh0PThiYTA1Oy9pbmRleC5waHA/dT02MDE3MDMmZXh0PTNkODg3IiwgImFib3V0IjogIjEuXHU5ZWQxXHU2NTk5XHU4OWM2XHU5ODkxXHU1M2VmXHU0ZWU1XHU3MGI5XHU1M2YzXHU0ZTBhXHU4OWQyXHU3NTI4XHU2ZDRmXHU4OWM4XHU1NjY4XHU2MjUzXHU1ZjAwXHU4OWMyXHU3NzBiXHVmZjBjXHU2NzJjQVBQXHU3NzBiXHU0ZTBkXHU0ZTg2XHVmZjBjXHU0ZTBkXHU3N2U1XHU5MDUzXHU5NWVlXHU5ODk4PGJyPjIuXHU1MjA2XHU0ZWFiXHU0ZTI0XHU0ZTJhMTAyNFx1OTA4MFx1OGJmN1x1NzgwMVx1ZmYxYVx1MzAxMGY2MTdiKmY2N2UwMzhmMWFcdTMwMTFcdTMwMTAyYyplNWFlMmUxYTU1NzIxXHUzMDExPGJyPjMuXHU5NjkwXHU4NWNmXHU1MTc2XHU0ZTJkXHU0ZTAwXHU0ZjRkXHVmZjBjXHU0ZTBkXHU1YjlhXHU2NWY2XHU1MjA2XHU0ZWFiXHU1MWUwXHU0ZTJhMTAyNFx1NzgwMVx1NWI1MFx1ZmYwY1x1NzUyOFx1NjIzN1x1NTQwZFx1NzUyOFx1NTE2OFx1NGUyZFx1NjU4N1x1NmNlOFx1NTE4Y1x1ZmYwMTxicj40Llx1NGUwZFx1ODk4MVx1NzUyOFVDL1x1NTkzOFx1NTE0Ylx1N2I0OVx1NTc4M1x1NTczZVx1NTZmZFx1NGVhN1x1NmQ0Zlx1ODljOFx1NTY2OFx1ZmYwY1x1NGUwZFx1NzEzNlx1NGY2MFx1NGYxYVx1NTNkMVx1NzNiMFx1NWY4OFx1NTkxYVx1N2Y1MVx1N2FkOVx1OTBmZFx1NGYxYVx1ODhhYlx1NWM0Zlx1ODUzZFx1ZmYwMTxicj41Llx1NjcyY0FQUFx1NmMzOFx1NGU0NVx1NTA1Y1x1NmI2Mlx1NjZmNFx1NjViMFx1ZmYwMVx1NjEzZlx1NGY2MFx1NWI4OVx1NTk3ZCIsICJoZWFkZXJfbXMiOiAiXHU4ZmQ5XHU5MWNjXHU2MDNiXHU2NzA5XHU0ZjYwXHU2MGYzXHU3NzBiXHU3Njg0XHU1NDI3IiwgImhlYWRlcl91cmwiOiAiIiwgImNhb2xpdV91cmwxIjogImh0dHBzOi8vY2wuNzU4NXgueHl6IiwgImNhb2xpdV91cmwyIjogImh0dHBzOi8vY2wuNjIxei54eXoiLCAiY2FvbGl1X3VybDMiOiAiaHR0cHM6Ly9jbC42MjF4Lnh5eiIsICJhcnRpY2xlX2FkIjogIiIsICJjb21taXRfYWQiOiAiIiwgInBvcm5fdmlkZW9fdXJsIjogImh0dHBzOi8vZjAzMTAuOTFwNDguY29tL2luZGV4LnBocCIsICJwb3JuX3ZpZGVvXzFhZCI6ICIiLCAicG9ybl92aWRlb18yYWQiOiAiIiwgInBvcm5fdmlkZW9fM2FkIjogIiIsICJwb3JuX3ZpZGVvXzRhZCI6ICIiLCAicG9ybl92aWRlb181YWQiOiAiIiwgInBvcm5fdmlkZW9fNmFkIjogIiIsICJwb3JuX3ZpZGVvX2Zvb3RlciI6ICIiLCAicG9ybl9pbWFnZV91cmwiOiAiaHR0cHM6Ly90MDMyOC53b25kZXJmdWxkYXkyNy5saXZlL2luZGV4LnBocCIsICJwb3JuX3Bob3RvX2hlYWRlciI6ICIiLCAicG9ybl9waG90b19oZWFkZXIyIjogIiIsICJwb3JuX3Bob3RvX2Zvb3RlciI6ICIiLCAicG9ybl9waG90b193ZW50b3UiOiAiIiwgImhlaWxpYW9fdXJsMSI6ICJodHRwczovL3p6dHQyNi5jb20vIiwgImhlaWxpYW9fdXJsMiI6ICJodHRwczovL3p6dHQyNy5jb20vIiwgImhlaWxpYW9fdXJsMyI6ICJodHRwczovL3p6dHQyOC5jb20vIiwgImhlaWxpYW9faGVhZGVyIjogIiIsICJoZWlsaWFvX2Zvb3RlciI6ICIiLCAiaGVpbGlhb19hcnRpY2FsIjogIiIsICJtYXppbm90ZSI6ICJcdTk3MDBcdTg5ODFcdTkwODBcdThiZjdcdTc4YmNcdThiZjdcdTkwYWVcdTdiYjE6Y2FvbGl1c2hlcXUyMDIyQDE2My5jb20iLCAic2VodWF0YW5nMSI6ICJodHRwczovL3pjZHNhZGUuY2ZkIiwgInNlaHVhdGFuZzIiOiAiaHR0cHM6Ly9mZHNhZmRzYWYuY28iLCAic2VodWF0YW5nMyI6ICJodHRwczovL2RzYWRzZmdkLmFydCIsICJqYXZidXMxIjogImh0dHBzOi8vd3d3LnNlZWphdi5wdyIsICJqYXZidXMyIjogImh0dHBzOi8vd3d3LmJ1c2phdi5mdW4iLCAiamF2YnVzMyI6ICJodHRwczovL3d3dy5qYXZzZWUuY2x1YiIsICJsdW50YW4yMDQ4MSI6ICJodHRwczovLzRzLmFhYTU2Ny5jb20vMjA0OC8iLCAibHVudGFuMjA0ODIiOiAiaHR0cHM6Ly8zcS5nb3V4aWU4LmNvbS8yMDQ4LyIsICJsdW50YW4yMDQ4MyI6ICJodHRwczovL2xzcC5zb3VhaXFpbi5jb20vMjA0OC8ifQ==pythonpython
      

posted @ 2022-04-03 21:37  愚哦  阅读(247)  评论(0编辑  收藏  举报