- Notifications
You must be signed in to change notification settings - Fork 648
Open
Labels
Description
贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 JS 基础之异步(三):Promise源码实现 查看
完整代码实现
// 三种状态constPENDING="pending"constFULFILLED="fulfilled"constREJECTED="rejected"functionMyPromise(callback){var_this=this_this.currentState=PENDING// Promise当前的状态_this.value=void0// Promise的值// 用于保存 then 的回调, 只有当 promise// 状态为 pending 时才会缓存,并且每个实例至多缓存一个_this.onResolvedCallbacks=[]// Promise resolve时的回调函数集_this.onRejectedCallbacks=[]// Promise reject时的回调函数集_this.resolve=function(value){if(valueinstanceofMyPromise){// 如果 value 是个 Promise, 递归执行returnvalue.then(_this.resolve,_this.reject)}setTimeout(()=>{// 异步执行,保证顺序执行if(_this.currentState===PENDING){_this.currentState=FULFILLED// 状态管理_this.value=value_this.onResolvedCallbacks.forEach(cb=>cb())}})}// resolve 处理函数_this.reject=function(value){setTimeout(()=>{// 异步执行,保证顺序执行if(_this.currentState===PENDING){_this.currentState=REJECTED// 状态管理_this.value=value_this.onRejectedCallbacks.forEach(cb=>cb())}})}// reject 处理函数// 异常处理// new Promise(() => throw Error('error'))try{callback(_this.resolve,_this.reject)// 执行callback并传入相应的参数}catch(e){_this.reject(e)}}// then 方法接受两个参数,onFulfilled,onRejected,分别为Promise成功或失败的回调MyPromise.prototype.then=function(onFulfilled,onRejected){var_this=this// 规范 2.2.7,then 必须返回一个新的 promisevarpromise2// 根据规范 2.2.1 ,onFulfilled、onRejected 都是可选参数// onFulfilled、onRejected不是函数需要忽略,同时也实现了值穿透onFulfilled=typeofonFulfilled==='function' ? onFulfilled : value=>valueonRejected=typeofonRejected==='function' ? onRejected : error=>{throwerror}if(_this.currentState===FULFILLED){// 如果promise1(此处为self/this)的状态已经确定并且为fulfilled,我们调用onFulfilled// 如果考虑到有可能throw,所以我们将其包在try/catch块中returnpromise2=newMyPromise(function(resolve,reject){try{varx=onFulfilled(_this.value)// 如果 onFulfilled 的返回值是一个 Promise 对象,直接取它的结果作为 promise2 的结果resolutionProcedure(promise2,x,resolve,reject)}catch(err){reject(err)// 如果出错,以捕获到的错误作为promise2的结果}})}// 此处实现与FULFILLED相似,区别在使用的是onRejected而不是onFulfilledif(_this.currentState===REJECTED){returnpromise2=newMyPromise(function(resolve,reject){try{varx=onRejected(_this.value)resolutionProcedure(promise2,x,resolve,reject)}catch(err){reject(err)}})}if(_this.currentState===PENDING){// 如果当前的Promise还处于PENDING状态,我们并不能确定调用onFulfilled还是onRejected// 只有等待Promise的状态确定后,再做处理// 所以我们需要把我们的两种情况的处理逻辑做成callback放入promise1(此处即_this/this)的回调数组内// 处理逻辑和以上相似returnpromise2=newMyPromise(function(resolve,reject){_this.onResolvedCallbacks.push(function(){try{varx=onFulfilled(_this.value)resolutionProcedure(promise2,x,resolve,reject)}catch(err){reject(err)}})_this.onRejectedCallbacks.push(function(){try{varx=onRejected(_this.value)resolutionProcedure(promise2,x,resolve,reject)}catch(err){reject(err)}})})}// 规范 2.3/* resolutionProcedure函数即为根据x的值来决定promise2的状态的函数 也即标准中的[Promise Resolution Procedure](https://promisesaplus.com/#point-47) x 为 promise2 = promise1.then(onFulfilled, onRejected)里onFulfilled/onRejected的返回值 resolve 和 reject 实际上是 promise2 的executor的两个实参,因为很难挂在其他地方,所以一并传过来。 相信各位一定可以对照标准转换成代码,这里就只标出代码在标准中对应的位置,只在必要的地方做一些解释。 */functionresolutionProcedure(promise2,x,resolve,reject){// 规范 2.3.1,x 不能和 promise2 相同,避免循环引用if(promise2===x){returnreject(newTypeError("Chaining cycle detected for promise!"))}// 规范 2.3.2// 如果 x 为 Promise,状态为 pending 需要继续等待否则执行if(xinstanceofMyPromise){// 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejectedif(x.currentState===PENDING){x.then(function(value){// 再次调用该函数是为了确认 x resolve 的// 参数是什么类型,如果是基本类型就再次 resolve// 把值传给下个 thenresolutionProcedure(promise2,value,resolve,reject)},reject)}else{// 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态x.then(resolve,reject)}return}letcalled=false// 规范 2.3.3,判断 x 是否为对象或函数if(x!==null&&(typeofx==="object"||typeofx==="function")){// 规范 2.3.3.2,如果不能取出 then,就 rejecttry{// 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用// 既要判断它的类型,又要调用它,这就是两次读取letthen=x.then// 规范2.3.3.3,如果 then 是函数,调用 x.thenif(typeofthen==="function"){// 规范 2.3.3.3// reject 或 reject 其中一个执行过的话,忽略其他的then.call(x,y=>{// 规范 2.3.3.3.1if(called)return// 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准called=true// 规范 2.3.3.3.1returnresolutionProcedure(promise2,y,resolve,reject)},r=>{if(called)return// 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准called=truereturnreject(r)})}else{// 规范 2.3.3.4resolve(x)}}catch(e){// 规范 2.3.3.2if(called)return// 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准called=truereturnreject(e)}}else{// 规范 2.3.4,x 为基本类型resolve(x)}}}// catch 的实现MyPromise.prototype.catch=function(onRejected){returnthis.then(null,onRejected)}// finally 的实现MyPromise.prototype.finally=function(callback){returnthis.then(function(value){returnMyPromise.resolve(callback()).then(function(){returnvalue})},function(err){returnMyPromise.resolve(callback()).then(function(){throwerr})})}// raceMyPromise.race=function(values){returnnewMyPromise(function(resolve,reject){values.forEach(function(value){MyPromise.resolve(value).then(resolve,reject)})})}// allMyPromise.all=function(arr){varargs=Array.prototype.slice.call(arr)returnnewMyPromise(function(resolve,reject){if(args.length===0)returnresolve([])varremaining=args.lengthfor(vari=0;i<args.length;i++){res(i,args[i])}functionres(i,val){if(val&&(typeofval==='object'||typeofval==='function')){if(valinstanceofMyPromise&&val.then===MyPromise.prototype.then){if(val.currentState===FULFILLED)returnres(i,val.value)if(val.currentState===REJECTED)reject(val.value)val.then(function(val){res(i,val)},reject)return}else{varthen=val.thenif(typeofthen==='function'){varp=newMyPromise(then.bind(val))p.then(function(val){res(i,val)},reject)return}}}args[i]=valif(--remaining===0){resolve(args)}}})}// allSettledMyPromise.allSettled=function(promises){returnnewMyPromise((resolve,reject)=>{promises=Array.isArray(promises) ? promises : []letlen=promises.lengthconstargslen=len// 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象if(len===0)returnresolve([])// 将传入的参数转化为数组,赋给args变量letargs=Array.prototype.slice.call(promises)// 计算当前是否所有的 promise 执行完成,执行完毕则resolveconstcompute=()=>{if(--len===0){resolve(args)}}functionresolvePromise(index,value){// 判断传入的是否是 promise 类型if(valueinstanceofMyPromise){constthen=value.thenthen.call(value,function(val){args[index]={status: 'fulfilled',value: val}compute()},function(e){args[index]={status: 'rejected',reason: e}compute()})}else{args[index]={status: 'fulfilled',value: value}compute()}}for(leti=0;i<argslen;i++){resolvePromise(i,args[i])}})}