说到 ES6,Promise 是绕不过的问题;如果说 ES6 的 Class 是基于 Javascript 原型继承的封装,那么 Promise 则是对 callback 回调机制的改进。这篇文章,不谈 Promise 的实际应用;聊一下 Promise 的实现原理,从最简单的解决方案入手,一步一步的自己实现一个 SimplePromise。
正文
从最简单的 Promise 初始化和使用入手:
const pro=new Promise ((res, rej)=> {})
pro.then(data=> {}, err=> {})
Promise 的构造函数如上,需要传递一个函数作为参数,这个函数有两个变量: resolve, reject。而 Promise 有不同的执行状态,分三种情况:Resolve, Reject, Pending。根据以上的信息,写出最基本的 SimplePromise 的类结构:
class SimplePromise{
constructor(handler){
this._status="PENDING"
handler(this._resolve.bind(this), this._reject.bind(this))//参数函数的作用域指向Class
}
_resolve(){}
_reject(){}
}
接下来思考一下_resolve 与_reject两个函数的作用。我们知道,Promise 根据 then 方法来执行回调,而 then 是根据状态判断要执行的回调函数。不难推导出,_resolve 与_reject正是根据handler的执行来进行状态变更的,而状态只能由Pending向Reslove或Rejected转换,所以有:
class SimplePromise{
constructor(handler){
...
}
_resolve(val){//异步返回的数据
if(this._status==="PENDING"){//保证状态的不可逆性
this._status="RESOLVED"
this._value=val
}
}
_reject(val){
if(this._status==="PENDING"){
this._status="REJECTED"
this._value=val
}
}
}
then的调用逻辑
下面分析 then 函数的逻辑,从调用入手:
pro.then(data=> {}, err=> {})
then 接收两个参数,第一个是执行成功调用的函数,第二个是执行失败调用的函数。
class SimplePromise{
constructor(handler){
...
}
_resolve(val){
...
}
_reject(val){
...
}
then(success, fail){
switch (this._status){
case "PENDING":
break;
case "RESOLVED":
success(this._value)
break;
case "REJECTED":
fail(this._value)
break;
}
}
}
以上实现了最简单的一个 Promise
测试代码:
const pro=new SimplePromise(function(res, rej) {
let random=Math.random() * 10
if(random > 5){
res("success")
}
else{
rej("fail")
}
})
pro.then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
})
当然,这不能算是一个 Promise,目前仅仅实现了根据状态调用不同的回调函数。还没有实现异步。
那如何实现异步呢?关键在于 then 函数,当判断_status为PENDING时,如何延后调用 success与fail函数,等待状态改变后再调用?
支持异步
这里采用数组来存储 fail 与 success 函数:
class SimplePromise{
constructor(handler){
this.status="PENDING"
this._onSuccess=[]//存储fail 与 success 函数
this._onFail=[]
handler(this._resolve.bind(this), this._reject.bind(this))
}
_resolve(val){
if(this.status==="PENDING"){
...
let temp
while(this._onSuccess.length > 0){//依次执行onSuccess中的回调函数
temp=this._onSuccess.shift()
temp(val)
}
}
}
_reject(val){
if(this.status==="PENDING"){
...
let temp
while(this._onFail.length > 0){
temp=this._onFail.shift()
temp(val)
}
}
}
then (success, fail){
switch (this.status){
case "PENDING":
this._onSuccess.push(success)
this._onFail.push(fail)
break;
...
}
}
}
使用 onSuccess 和 onFail 来存储回调函数,当处理状态为 PENDING 时,将回调函数 push 到相应的数组里,当状态变更后,依次执行数组里的回调函数。
测试代码:
const pro=new SimplePromise(function(res, rej) {
setTimeout(function(){
let random=Math.random() * 10
if(random > 5){
res("success")
}
else{
rej("fail")
}
}, 2000)
})
pro.then(function(data) {
console.log(data)
}, function(err) {
console.log(err)
})
两秒后,会执行相应的回调。
到目前为止,最最最简单的一个 Promise 骨架已经基本完成了。但是还有很多功能待完成。现在可以稍微休息一下,喝个咖啡打个鸡血,回来我们会继续让这个 Promise 骨架更加丰满起来。
. . . . . .
完善Promise
欢迎回来,下面我们继续完善我们的 Promise。
上面完成了一个最基础的 Promise,然而还远远不够。首先,Promise 需要实现二手域名购买平台链式调用,其次 Promise 还需要实现 all race resolve reject 等静态函数。
首先,如何实现 then 的链式调用呢?需要 then 返回的也是一个 Promise。
于是有
class SimplePromise{
...
then(success, fail){
return new SimplePromise((nextSuccess, nextFail)=> {
const onFullfil=function(val){
const res=success(val)
nextSuccess(res)
}
const onReject=function(val){
const res=fail(val)
nextSuccess(res) ;
}
switch (this._status){
case "PENDING":
this._onSuccess.push(onFullfil)
this._onFail.push(onReject)
break;
case "RESOLVED":
onFullfil(this._value)
break;
case "REJECTED":
onReject(this._value)
break;
}
})
}
}
测试代码:
const sp=new SimplePromise(function (res, rej){
setTimeout(function(){
let random=Math.random() * 10
random > 5 res(random) : rej(random)
}, 1000)
})
sp.then(data=> {
console.log("more than 5 " + data)
return data
}, err=>{
console.log("less than 5 " + err)
return err
}).then((data)=> {
console.log(data)
})
then的参数限制
完成了链式调用,then 方法还有许多其他限制:
下面思考 以下问题:代码中四个使用 promise 的语句之间的不同点在哪儿?
假设 doSomething 也 doSomethingElse 都返回 Promise
doSomething().then(function () {
return doSomethingElse();
}).then(finalHandler);
doSomething().then(function () {
doSomethingElse();
}).then(finalHandler);;
doSomething().then(doSomethingElse()).then(finalHandler);;
doSomething().then(doSomethingElse).then(finalHandler);;
答案 一会儿再揭晓,我们先来梳理以下then 方法对传入不同类型参数的处理机制:
直接上代码:
class SimplePromise{
...
then(success, fail){
return new SimplePromise((nextSuccess, nextFail)=> {
const onFullfil=function(val){
if(typeof success !=="function"){
nextSuccess(val)
}
else{
const res=success(val)//success 的返回值
if(res instanceof SimplePromise){//如果success 返回一个promise 对象
res.then(nextSuccess, nextFail)
}
else{
nextSuccess(res)
}
}
}
if(fail){
const onReject=function(val){
if(typeof fail !=="function"){
nextSuccess(val)
}
else{
const res=fail(val)
if(res instanceof SimplePromise){
res.then(nextSuccess, nextFail)
}
else{
nextSuccess(res)
}
}
}
}
else{
onReject=function(){}
}
switch (this._status){
case "PENDING":
this._onSuccess.push(onFullfil)
this._onFail.push(onReject)
break;
case "RESOLVED":
onFullfil(this._value)
break;
case "REJECTED":
onReject(this._value)
break;
}
})
}
}
对于传入 then 方法的参数,首先判断其是否为 function,判断为否,直接执行 下一个 then 的 success 函数;判断为是,接着判断函数的返回值 res 类型是否为 Promise,如果为否,直接执行下一个 then 的 success 函数,如果为是,通过 then 调用接下来的函数。
所以,上面的问题就不难得到答案了。
doSomething().then(function () {
return doSomethingElse();//返回值为Promise
}).then(finalHandler);
RETURN:
doSomething
--->doSomethingElse(undefined)
---> final(doSomethingElseResult)
doSomething().then(function () {
doSomethingElse();//返回值为 undefined
}).then(finalHandler);
RETURN:
doSomething
--->doSomethingElse(undefined)
---> final(undefined)
doSomething().then(doSomethingElse())//参数 typeof !=function
.then(finalHandler);
RETURN:
doSomething
doSomethingElse(undefined)
---> final(doSomethingResult)
doSomething().then(doSomethingElse)//与1的调用方式是不同的
.then(finalHandler);
RETURN:
doSomething
--->doSomethingElse(doSomethingResult)
---> final(doSomethingElseResult)
好,then 方法已经完善好了。
静态函数
接下来是 Promise 的各种静态函数
class SimplePromise(){
...
static all(){}
static race(){}
static resolve(){}
static reject(){}
}
all
static all(promiselist){
if(Array.isArray(promiselist)){
const len=promiselist.length;
const count=0
const arr=[]
return new SimplePromise((res, rej)=> {
for(let i=0; i {
arr[i]=data
count ++
if(count===len){//每一个Promise都执行完毕后返回
res(arr)
}
}, err=> {
rej(err)
})
}
})
}
}
race
static race(promiselist){
if(Array.isArray(promiselist)){
const len=promiselist.length
return new SimplePromise((res, rej)=>{
promiselist.forEach(item=>{
this.resolve(item).then(data=> {
res(data)
}, err=>{
rej(err)
})
})
})
}
}
resolve
static resolve(obj){
if(obj instanceof SimplePromise){
return obj
}
else {
return new SimplePromise((res)=>{
res(obj)
})
}
}
reject
static reject(obj){
if(obj instanceof SimplePromise){
return obj
}
else {
return new SimplePromise((res, rej)=>{
rej(obj)
})
}
}
总结
现在,一个完整的 Promise 对象就完成了。现在来总结一下 callback 回调和 Promise 的异同吧。
其实,不管是 callback 还是 Promise,这二者都是将需要滞后执行方法而提前声明的方式,只不过 callback 的处理方式比较粗犷,将 cb 函数放到异步执行的结尾;而 Promise 优于 cb 的是通过定义了不同的执行状态,更加细致的进行结果处理,提供了很好的 catch 机制,这是其一;其二,then 的链式调用解决了 cb 的回调地狱;但是 then 的链式调用也不是很好的解决方案,如果封装不好,then里面套用大量的代码的话也会引起代码的不美观和阅读上的困难,这一方面的终极解决方法还是 es7 的 async/await。