promise实现原理

Posted yayaxuping

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了promise实现原理相关的知识,希望对你有一定的参考价值。

简要介绍:Promise允许我们通过链式调用的方式来解决“回调地狱”的问题,特别是在异步过程中,通过Promise可以保证代码的整洁性和可读性。本文主要解读Promise/A+规范,并在此规范的基础上,自己实现一个Promise.

一、Promise的使用

在了解Promise规范之前,我们知道主流的高版本浏览器已经支持ECMA中的Promise.

创建一个promise实例:

var p=new Promise(function(resolve,reject){     setTimeout(function(){        resolve("success")     },1000);     console.log("创建一个新的promise"); }) p.then(function(x){   console.log(x) })  //输出: 创建一个新的promise success 

上述是一个promise的实例,输出内容为,“创建一个promise”,延迟1000ms后,输出"success"。

从上述的例子可以看出,promise方便处理异步操作。此外promise还可以链式的调用:

var p=new Promise(function(resolve,reject){resolve()}); p.then(...).then(...).then(...) 

此外Promise除了then方法外,还提供了Promise.resolve、Promise.all、Promise.race等等方法。

二、Promise/A+规范

Promise/A+规范扩展了早期的Promise/A proposal提案,我们来解读一下Promise/A+规范。

1.术语

(1)"promise"是一个对象或者函数,该对象或者函数有一个then方法

(2)"thenable"是一个对象或者函数,用来定义then方法

(3)"value"是promise状态成功时的值

(4)"reason"是promise状态失败时的值

我们明确术语的目的,是为了在自己实现promise时,保持代码的规范性(也可以跳过此小节)

2.要求

(1)一个promise必须有3个状态,pending,fulfilled(resolved),rejected当处于pending状态的时候,可以转移到fulfilled(resolved)或者rejected状态。当处于fulfilled(resolved)状态或者rejected状态的时候,就不可变。

promise英文译为承诺,也就是说promise的状态一旦发生改变,就永远是不可逆的。

(2)一个promise必须有一个then方法,then方法接受两个参数:

promise.then(onFulfilled,onRejected) 

其中onFulfilled方法表示状态从pending——>fulfilled(resolved)时所执行的方法,而onRejected表示状态从pending——>rejected所执行的方法。

(3)为了实现链式调用,then方法必须返回一个promise

promise2=promise1.then(onFulfilled,onRejected) 

三、实现一个符合Promise/A+规范的Promise

解读了Promise/A+规范之后,下面我们来看如何实现一个Promise,
首先构造一个myPromise函数,关于所有变量和函数名,应该与规范中保持相同。

1.v1.0 初始版本myPromise

function myPromise(constructor){     let self=this;     self.status="pending" //定义状态改变前的初始状态     self.value=undefined;//定义状态为resolved的时候的状态     self.reason=undefined;//定义状态为rejected的时候的状态     function resolve(value){         //两个==="pending",保证了状态的改变是不可逆的        if(self.status==="pending"){           self.value=value;           self.status="resolved";        }     }     function reject(reason){         //两个==="pending",保证了状态的改变是不可逆的        if(self.status==="pending"){           self.reason=reason;           self.status="rejected";        }     }     //捕获构造异常     try{        constructor(resolve,reject);     }catch(e){        reject(e);     } } 

同时,需要在myPromise的原型上定义链式调用的then方法:

myPromise.prototype.then=function(onFullfilled,onRejected){    let self=this;    switch(self.status){       case "resolved":         onFullfilled(self.value);         break;       case "rejected":         onRejected(self.reason);         break;       default:           } } 

上述就是一个初始版本的myPromise,在myPromise里发生状态改变,然后在相应的then方法里面根据不同的状态可以执行不同的操作。

var p=new myPromise(function(resolve,reject){resolve(1)}); p.then(function(x){console.log(x)}) //输出1 

但是这里myPromise无法处理异步的resolve.比如:

var p=new myPromise(function(resolve,reject){setTimeout(function(){resolve(1)},1000)});  p.then(function(x){console.log(x)}) //无输出 

2.v2.0基于观察模式实现

为了处理异步resolve,我们修改myPromise的定义,用2个数组onFullfilledArray和onRejectedArray来保存异步的方法。在状态发生改变时,一次遍历执行数组中的方法。

function myPromise(constructor){     let self=this;     self.status="pending" //定义状态改变前的初始状态     self.value=undefined;//定义状态为resolved的时候的状态     self.reason=undefined;//定义状态为rejected的时候的状态     self.onFullfilledArray=[];     self.onRejectedArray=[];     function resolve(value){        if(self.status==="pending"){           self.value=value;           self.status="resolved";           self.onFullfilledArray.forEach(function(f){                 f(self.value);                 //如果状态从pending变为resolved,                 //那么就遍历执行里面的异步方法           });                 }     }     function reject(reason){        if(self.status==="pending"){           self.reason=reason;           self.status="rejected";           self.onRejectedArray.forEach(function(f){               f(self.reason);              //如果状态从pending变为rejected,               //那么就遍历执行里面的异步方法           })        }     }     //捕获构造异常     try{        constructor(resolve,reject);     }catch(e){        reject(e);     } } 

对于then方法,状态为pending时,往数组里面添加方法:

myPromise.prototype.then=function(onFullfilled,onRejected){    let self=this;    switch(self.status){       case "pending":         self.onFullfilledArray.push(function(){              onFullfilled(self.value)         });         self.onRejectedArray.push(function(){              onRejected(self.reason)         });       case "resolved":         onFullfilled(self.value);         break;       case "rejected":         onRejected(self.reason);         break;       default:           } } 

这样,通过两个数组,在状态发生改变之后再开始执行,这样可以处理异步resolve无法调用的问题。这个版本的myPromise就能处理所有的异步,那么这样做就完整了吗?

没有,我们做Promise/A+规范的最大的特点就是链式调用,也就是说then方法返回的应该是一个promise。

3.v3.0then方法实现链式调用

要通过then方法实现链式调用,那么也就是说then方法每次调用需要返回一个primise,同时在返回promise的构造体里面,增加错误处理部分,我们来改造then方法

myPromise.prototype.then=function(onFullfilled,onRejected){     let self=this;     let promise2;     switch(self.status){       case "pending":         promise2=new myPromise(function(resolve,reject){              self.onFullfilledArray.push(function(){                 try{                    let temple=onFullfilled(self.value);                    resolve(temple)                 }catch(e){                    reject(e) //error catch                 }              });              self.onRejectedArray.push(function(){                  try{                    let temple=onRejected(self.reason);                    reject(temple)                  }catch(e){                    reject(e)// error catch                  }              });         })       case "resolved":         promise2=new myPromise(function(resolve,reject){             try{               let temple=onFullfilled(self.value);               //将上次一then里面的方法传递进下一个Promise的状态               resolve(temple);             }catch(e){               reject(e);//error catch             }         })         break;       case "rejected":         promise2=new myPromise(function(resolve,reject){             try{                let temple=onRejected(self.reason);                //将then里面的方法传递到下一个Promise的状态里                resolve(temple);                }catch(e){                reject(e);             }         })         break;       default:           }    return promise2; } 

这样通过then方法返回一个promise就可以实现链式的调用:

p.then(function(x){console.log(x)}).then(function(){console.log("链式调用1")}).then(function(){console.log("链式调用2")}) //输出 1 链式调用1 链式调用2 

这样我们虽然实现了then函数的链式调用,但是还有一个问题,就是在Promise/A+规范中then函数里面的onFullfilled方法和onRejected方法的返回值可以是对象,函数,甚至是另一个promise。

4.v4.0 then函数中的onFullfilled和onRejected方法的返回值问题

特别的为了解决onFullfilled和onRejected方法的返回值可能是一个promise的问题。

(1)首先来看promise中对于onFullfilled函数的返回值的要求

I)如果onFullfilled函数返回的是该promise本身,那么会抛出类型错误

II)如果onFullfilled函数返回的是一个不同的promise,那么执行该promise的then函数,在then函数里将这个promise的状态转移给新的promise
III)如果返回的是一个嵌套类型的promsie,那么需要递归。

IV)如果返回的是非promsie的对象或者函数,那么会选择直接将该对象或者函数,给新的promise。

根据上述返回值的要求,我们要重新的定义resolve函数,这里Promise/A+规范里面称为:resolvePromise函数,该函数接受当前的promise、onFullfilled函数或者onRejected函数的返回值、resolve和reject作为参数。

下面我们来看resolvePromise函数的定义:

function resolvePromise(promise,x,resolve,reject){   if(promise===x){      throw new TypeError("type error")   }   let isUsed;   if(x!==null&&(typeof x==="object"||typeof x==="function")){       try{         let then=x.then;         if(typeof then==="function"){            //是一个promise的情况            then.call(x,function(y){               if(isUsed)return;               isUsed=true;               resolvePromise(promise,y,resolve,reject);            },function(e){               if(isUsed)return;               isUsed=true;               reject(e);            })         }else{            //仅仅是一个函数或者是对象            resolve(x)         }       }catch(e){          if(isUsed)return;          isUsed=true;          reject(e);       }   }else{     //返回的基本类型,直接resolve     resolve(x)   } } 

改变了resolvePromise函数之后,我们在then方法里面的调用也变成了resolvePromise而不是promise。

myPromise.prototype.then=function(onFullfilled,onRejected){     let self=this;     let promise2;     switch(self.status){       case "pending":         promise2=new myPromise(function(resolve,reject){              self.onFullfilledArray.push(function(){                 setTimeout(function(){                   try{ 	                   let temple=onFullfilled(self.value); 	                   resolvePromise(temple) 	                }catch(e){ 	                   reject(e) //error catch 	                }                 })              });              self.onRejectedArray.push(function(){                 setTimeout(function(){                    try{ 	                   let temple=onRejected(self.reason); 	                   resolvePromise(temple) 	                 }catch(e){ 	                   reject(e)// error catch 	               }                 })              });         })       case "resolved":         promise2=new myPromise(function(resolve,reject){            setTimeout(function(){                try{ 	              let temple=onFullfilled(self.value); 	              //将上次一then里面的方法传递进下一个Promise状态 	              resolvePromise(temple); 	            }catch(e){                   reject(e);//error catch                }            })         })         break;       case "rejected":         promise2=new myPromise(function(resolve,reject){            setTimeout(function(){              try{                let temple=onRejected(self.reason);                //将then里面的方法传递到下一个Promise的状态里                resolvePromise(temple);                 }catch(e){                reject(e);              }            })         })         break;       default:           }    return promise2; } 

这样就能处理onFullfilled各种返回值的情况。

var p=new Promise(function(resolve,reject){resolve("初始化promise")}) p.then(function(){return new Promise(function(resolve,reject){resolve("then里面的promise返回值")})}).then(function(x){console.log(x)})  //输出 then里面promise的返回值 

到这里可能有点乱,我们再理一理,首先返回值有两个:

  • then函数的返回值——>返回一个新promise,从而实现链式调用

  • then函数中的onFullfilled和onRejected方法——>返回基本值或者新的promise

这两者其实是有关联的,onFullfilled方法的返回值可以决定then函数的返回值。



以上是关于promise实现原理的主要内容,如果未能解决你的问题,请参考以下文章

promise实现原理代码

Promise原理及实现

Promise原理及实现

promise实现原理

Promise 原理

前端面试题之手写promise