原文在个人博客中:原文地址 若是文章对您有帮助,您的star是对我最好的鼓励~git
简要介绍:Promise容许咱们经过链式调用的方式来解决“回调地狱”的问题,特别是在异步过程当中,经过Promise能够保证代码的整洁性和可读性。本文主要解读Promise/A+规范,并在此规范的基础上,本身实现一个Promise.github
在了解Promise规范以前,咱们知道主流的高版本浏览器已经支持ECMA中的Promise.npm
建立一个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方便处理异步操做。此外promise还能够链式的调用:浏览器
var p=new Promise(function(resolve,reject){resolve()});
p.then(...).then(...).then(...)
复制代码
此外Promise除了then方法外,还提供了Promise.resolve、Promise.all、Promise.race等等方法。bash
Promise/A+规范扩展了早期的Promise/A proposal提案,咱们来解读一下Promise/A+规范。异步
(1)"promise"是一个对象或者函数,该对象或者函数有一个then方法async
(2)"thenable"是一个对象或者函数,用来定义then方法函数
(3)"value"是promise状态成功时的值
(4)"reason"是promise状态失败时的值
咱们明确术语的目的,是为了在本身实现promise时,保持代码的规范性(也能够跳过此小节)
(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, 首先构造一个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)})
//无输出
复制代码
为了处理异步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。
要经过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。
特别的为了解决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函数的返回值。
npm install -g promises-aplus-tests
复制代码
具体用法请看promise test而后
promises-aplus-tests myPromise.js
复制代码
结果为:
完整代码的地址
https://github.com/forthealllight/promise-achieve
interface IConstructor{
(resolve:IResolve,reject:IReject):void
}
interface IResolve {
(x:any):void
}
interface IReject {
(x:any):void
}
function myPromise(constructor:IConstructor):void{
let self:object=this;
self.status="pending";
self.value=undefined;//if pending->resolved
self.reason=undefined;//if pending->rejected
self.onFullfilledArray=[];//to deal with async(resolved)
self.onRejectedArray=[];//to deal with async(rejeced)
let resolve:IResolve;
resolve=function(value:any):void{
//pending->resolved
if(self.status==="pending"){
self.status="resolved";
self.value=value;
self.onFullfilledArray.forEach(function(f){
f(self.value);
})
}
}
let reject:IReject;
reject=function(reason:any):void{
if(self.status==="pending"){
self.status="rejected";
self.reason=reason;
self.onRejectedArray.forEach(function(f){
f(self.reason);
})
}
}
//According to the definition that the function "constructor" accept two parameters
//error catch
try {
constructor(resolve,reject);
} catch (e) {
reject(e);
}
}
复制代码
单纯的写个工具函数,用ts仍是有点影响可读性。