You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
letreject=(reason)=>{if(this.status===PENDING){this.status=REJECTED;this.reason=reason;this.onRejectedCallbacks.forEach((fn)=>fn());}};letresolve=(value)=>{// 如果 value 是一个promise,那我们的库中应该也要实现一个递归解析if(valueinstanceofPromise){// 递归解析returnvalue.then(resolve,reject);}if(this.status===PENDING){this.status=FULFILLED;this.value=value;this.onResolvedCallbacks.forEach((fn)=>fn());}};
测试例子:
constpromise1=Promise.resolve(123);promise1.then((value)=>{console.log(value);// expected output: 123});Promise.resolve(newPromise((resolve,reject)=>{setTimeout(()=>{if(Math.random()>0.5){resolve("ok");}else{reject("err");}},3000);})).then((data)=>{console.log(data,"success");// ok success}).catch((err)=>{console.log(err,"error");// err error});
Promise.all=function(values){if(!Array.isArray(values)){consttype=typeofvalues;returnnewTypeError(`TypeError: ${type}${values} is not iterable`);}returnnewPromise((resolve,reject)=>{letresultArr=[];letorderIndex=0;constprocessResultByKey=(value,index)=>{resultArr[index]=value;if(++orderIndex===values.length){resolve(resultArr);}};for(leti=0;i<values.length;i++){letvalue=values[i];if(value&&typeofvalue.then==="function"){value.then((value)=>{processResultByKey(value,i);},reject);}else{processResultByKey(value,i);}}});};
前言
手写 Promise 是面试的时候大家都逃避的送命题,在学些了解后发现通过实现源码更能将新一代的异步方案理解的通透,知其然知其所以然的运用。
如果直接将源码贴到此处势必不能有更大的收获,下面就按实现版本来看做简要分析。
回顾 Promise
Promise 是 CommonJS 提出来的这一种规范,有多个版本,在 ES6 当中已经纳入规范,原生支持 Promise 对象,非 ES6 环境可以用类似 Bluebird、Q 这类库来支持。
Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅,还可以批量处理异步任务。
简单归纳下 Promise:三个状态、两个过程、一个方法,快速记忆方法:3-2-1
三个状态:pending、fulfilled、rejected
两个过程:
一个方法:then
当然还有其他概念,如 catch、 Promise.all/race/allSettled。
基础版
基础测试用例
Promise 的基本特征
手写基础版
测试用例:
存在的问题:
认识 Promise /A+ 规范
手写 Promise,需要遵守怎样的规则,业界所有 Promise 的类库都遵循 Promise/A+ 规范。译文
改进版
先按 Promise 的基本规范对上面的基础版进行改进。
executor
,executor
接受两个参数,分别是 resolve 和 reject;pending
,fulfilled
,orrejected
, 默认状态是pending
,只能从pending
到rejected
, 或者从pending
到fulfilled
,状态一旦确认,就不会再改变;「规范 Promise/A+ 2.1」value
保存成功状态的值,可以是undefined/thenable/promise
;「规范 Promise/A+ 1.3」reason
保存失败状态的值;「规范 Promise/A+ 1.5」onFulfilled
,参数是 promise 的value
;onRejected
, 参数是 promise 的reason
;Promise A+规范版
规范思路梳理
如果调用 then 时,promise 已经成功,则执行 onFulfilled,并将 promise 的值作为参数传递进去。 如果 promise 已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。如果 promise 的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
onFulfilled
和onRejected
可以缺省,如果onFulfilled
或者onRejected
不是函数,将其忽略,且依旧可以在下面的then
中获取到之前返回的值;「规范 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2」源码实现
测试一下,打印日志也对了:
再试试 then 的链式调用和值的穿透:
完善 Promise API
虽然上述的 promise 源码已经符合 Promise/A+ 的规范,但是原生的 Promise 还提供了一些其他方法,如:
Promise.prototype.catch()
Promise.prototype.finally()
Promise.resolve(value)
—— 使用给定 value 创建一个 resolved 的 promise。Promise.reject(error)
—— 使用给定 error 创建一个 rejected 的 promise。Promise.all(promises)
—— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成 Promise.all 的 error,所有其他 promise 的结果都会被忽略。Promise.race(promises)
—— 等待第一个 settle 的 promise,并将其 result/error 作为结果返回。Promise.any(promises)
(ES2021 新增方法)—— 等待第一个 fulfilled 的 promise,并将其结果作为结果返回。如果所有 promise 都 rejected,Promise.any 则会抛出 AggregateError 错误类型的 error 实例。Promise.allSettled(promises)
(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:下面具体说一下每个方法的实现:
Promise.prototype.catch
先从错误捕获开始,catch 用来捕获 promise 的异常,就相当于一个没有成功的 then。
测试例子:
Promise.prototype.finally()
finally 方法,在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。
这避免了同样的语句需要在 then()和 catch()中各写一次的情况。
测试用例:
Promise.resolve
默认产生一个成功的 promise。Promise.resolve(value)方法返回一个以给定值解析后的 Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是 thenable(即带有"then" 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态;否则返回的 promise 将以此值完成。此函数将类 promise 对象的多层嵌套展平。
如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在 constructor 方法中做一个小小的处理:
测试例子:
Promise.reject()
默认产生一个失败的 promise,Promise.reject 是直接将值变成错误结果。
Promise.all()
promise.all 是解决并发问题的,多个异步并发获取最终的结果(如果有一个失败则失败)。
测试一下:
Promise.race()
Promise.race 用来处理多个请求,采用最快的(谁先完成用谁的)。
这里第一个 promise 最快,所以它变成了结果。第一个 settled 的 promise “赢得了比赛”之后,所有进一步的 result/error 都会被忽略。
例如,这里的结果将是 1:
Promise.any()
与 Promise.race 类似,区别在于 Promise.any 只等待第一个 fulfilled 的 promise,并将这个 fulfilled 的 promise 返回,它不会等待其他的 promise 全部完成。如果给出的 promise 都 rejected,那么则返回 rejected 的 promise 和 AggregateError 错误类型的 error 实例—— 一个特殊的 error 对象,在其 errors 属性中存储着所有 promise error。
例如,这里的结果将是 1:
这里的第一个 promise 是最快的,但 rejected 了,所以第二个 promise 则成为了结果。在第一个 fulfilled 的 promise “赢得比赛”后,所有进一步的结果都将被忽略。
这是一个所有 promise 都失败的例子:
正如你所看到的,我们在 AggregateError 错误类型的 error 实例的 errors 属性中可以访问到失败的 promise 的 error 对象。
实现该方法,可以通过上面的两个例子:
Promise.any 只要传入的 promise 有一个是 fullfilled 则立即 resolve 出去,否则将所有 reject 结果收集起来并返回 AggregateError
Promise.allSettled()
如果任意的 promise reject,则 Promise.all 整个将会 reject。当我们需要 所有 结果都成功时,它对这种“全有或全无”的情况很有用:Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:
测试一下:
参考文章
The text was updated successfully, but these errors were encountered: