前言
promise是前端同步代码执行机制的一个重点手段。也许你今天用的已经是es7的await,但理解好什么是promise依然是前端的重点。我们逐步分析一下promise的实现。
再同步一下笔者的博客源码系列的进度:
| 序号 | 博客主题 | 相关链接 | |-----|------|------------|- | 1 | 手写vue_mini源码解析 | juejin.cn/post/684790… | | 2 | 手写react_mini源码解析 | juejin.cn/post/685457… | | 3 | 手写webpack_mini源码解析 | juejin.cn/post/685457… | | 4 | 手写jquery_mini源码解析| juejin.cn/post/685457… | | 5 | 手写vuex_mini源码解析(即本文) | juejin.cn/post/685529… | | 6 | 手写vue_route源码解析 | juejin.cn/post/685956… | | 7 | 手写diff算法源码解析 | juejin.cn/post/686881… | | 8 | 手写promise源码解析 | juejin.cn/post/686920… | | 9 | 手写原生js源码解析(手动实现常见api) | 预计9月 | | 10 | 手写react_redux,fiberd源码解析等 | 预计9月| | 11 | 手写koa2_mini | 预计9月,前端优先 |
promise的概念
在传统的项目中,前端解决异步问题,只能用无止境的callback回调。其实小的项目,或者简单点的逻辑,callback无疑是最高效的。然后项目逐步扩大时,就会出现,所谓的回调地狱。可能我们无法定位到入口在哪里,可能也会出现调用重复的问题等等,且问题十分不易跟踪,这时候社区(或者ES6规范)就引入了几个异步的解决方案:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
- await
其中,Promise 是设计用于改善 JS 中的异步编程,与事件及回调函数对比,在异步操作方面为你提供了更多的控制权与组合性。 Promise 调度被添加到 JS 引擎作业队列,以便稍后执行。不过此处有另一个作业队列追踪着 Promise 的完成与拒绝处理函数,以确保适当的执行。
当然,promise也有一些缺陷:
- 1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 3)当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
- 4)then的写法相比await,明显在程序代码抒写上,更加繁琐。
我们下述,手写一下promise分析一下整个过程。
手写promise/A+的过程
Promise/A+并未规范race、all、catch方法,这些是ES6自己规范的。本文重点在Promise本身,故重点在于Promise自身的构建,以及then函数的实现。
构造函数
我们先分析一下构造函数需要处理什么?
- 1.Promise有是三个状态,分别为pedding, rejected, refulled。默认为pedding。 首先我们要记录这个状态,_status。
- 2.每一个Promise,都有自己的resolve跟reject方法,我们定义asynFulliled,asynRejected 如果状态转换为rejected时候,调用reject。 如果状态转换为refulled时候,调用resolve
- 3.执行完当前的Promise,如果程序正常,将进入下一步的resolve跟reject方法,即nextResolve跟nextReject。
- 4.调用下一个Promise时,当前Promise的值,将传为下一个的Promise。我们定义_param来存储他。
根据分析,我们可以简单列出下述代码:
class ZPromise{
constructor(){
var self = this;
self._param = null;//返回值
self._status = PEDDING;//添加状态
//then方法返回的promise对象的resovle和reject
self.nextResolve = null;//
self.nextReject = null;//
//记录then的方法参数
self.asynFulliled = null;
self.asynRejected = null;
self.then = function( onFulliled, onReject ){
//
}
}
}
首次执行handle方法
我们再来看看,如何新建一个promise或者then方法的:
new ZPromise(function (resolve, reject) {
...
}).then( function(result){
...
})
构造首次执行后,首先需要执行第一个...的东西。我们把他定义为handle。此时,第一个Promise可能会执行resolve()或者reject()。那么这个resolve或者或者reject是个什么东西么?我们来写一个_resolve与_reject实现他。
- 首先resolve(或reject)是不是会改变_status状态。例如resolve之后,promise的状态,将变化为rejected。
- 其次,下一个promise传入的参数,即使上一个promise的resolve的值,我们用_param来储存他。
- 如果此时有下一个promise,将继续调用下一个promise的方法(下述then的讲述,会将下一个promise的方法告诉该函数)
修改一下构造函数:
class ZPromise{
constructor(handle){
...
handle( _resolve, _reject );
}
}
function _resolve( val ){
self._status = REFULLED;
self._param = val;
}
function _reject( error ){
_status = REJECTED;
_param = error;
}
then方法
我们再分析then方法后处理什么?
-
首先,then方法也是一个promise,如果不是,我们需要帮他构造成promise。即指向self对象的constructor原型。
-
then方法,需要根据对象的状态:
-
如果是上一个promise是失败状态,这调用失败的回调(我们叫它:doAsynRejected);
-
如果是上一个promise是成功状态,这调用成功的回调(我们叫它:doAsynFulliled);
-
如果还在执行中,我们将当前的对象重新赋值于nextResolve,nextReject,asynFulliled,asynRejected
self.then = function( onFulliled, onReject ){ return new self.constructor( function( resovle, reject ){ if( self._status == REFULLED ){ doAsynFulliled( onFulliled, resovle, reject ); } else if ( self._status == REFULLED ){ doAsynRejected( onReject, resovle, reject ); } else {//即pedding的时候 往上执行 self.nextResolve = resovle; self.nextReject = reject; self.asynFulliled = onFulliled; self.asynRejected = onReject; } }) }
在handle首次初始化之前,此时的状态肯定是pedding, 为了方便下一个promise(即是then中的那个promise)知道对应的,resovle跟reject,还有对应的promise, 我们分别储存起来(promise分别对应的成功与失败)。
假设成功,那么我们即构建下一个promise,此时调用doAsynFulliled。
-
此时下一个promise依然是个构造函数(即function),我们则重新建立一个promise, 否则说明已经结束,resolve返回最后的值即可。
-
此时如果下一个promise的then中含有另外一个promise,则需要等他那个promise全部执行完毕。那我们需要等内部的promise执行完毕。
function doAsynFulliled( onFulliled, resolve, reject ){ if( typeof onFulliled == 'function' ){ let promise = onFulliled( self._param ); if( promise == undefined ){//说明首次 resolve( self._param ); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { resolve( self._param ); } } else { resolve( self._param ); } }
同理:
function doAsynRejected( onRejected, resolve, reject ){
if( typeof onRejected == 'function' ){
let promise = onFulliled( self._param );
if( promise == undefined ){//说明首次
reject( self._param );
} else if ( promise.constructor == self.constructor ){
promise.then( function(param){
resolve( param );
}, function( param ){
reject( param );
})
} else {
reject( self._param );
}
} else {
reject( self._param );
}
}
我们整理一下:
var PEDDING = "pedding";
var REJECTED = "rejected";
var REFULLED = "refulled";
class MyPromise{
constructor(handle){
if( typeof("handle") == "function" ){
throw new Error("MyPromise handle is not a function");
}
var self = this;
self._param = null;//返回值
self._status = PEDDING;//添加状态
//then方法返回的promise对象的resovle和reject
self.nextResolve = null;//
self.nextReject = null;//
//记录then的方法参数
self.asynFulliled = null;
self.asynRejected = null;
handle( _resolve, _reject );
self.then = function( onFulliled, onReject ){
return new self.constructor( function( resovle, reject ){
if( self._status == REFULLED ){
doAsynFulliled( onFulliled, resovle, reject );
} else if ( self._status == REFULLED ){
doAsynRejected( onReject, resovle, reject );
} else {//即pedding的时候 往上执行
self.nextResolve = resovle;
self.nextReject = reject;
self.asynFulliled = onFulliled;
self.asynRejected = onReject;
}
})
}
function _resolve( val ){
self._status = REFULLED;
self._param = val;
if( self.nextResolve ){
doAsynFulliled( self.asynFulliled, self.nextResolve, self.nextReject );
}
}
function _reject( error ){
_status = REJECTED;
_param = error;
if( nextReject ){
doAsynRejected( asynRejected, nextResolve, nextReject );
}
}
function doAsynFulliled( onFulliled, resolve, reject ){
if( typeof onFulliled == 'function' ){
let promise = onFulliled( self._param );
if( promise == undefined ){//说明首次
resolve( self._param );
} else if ( promise.constructor == self.constructor ){
promise.then( function(param){
resolve( param );
}, function( param ){
reject( param );
})
} else {
resolve( self._param );
}
} else {
resolve( self._param );
}
}
function doAsynRejected( onRejected, resolve, reject ){
if( typeof onRejected == 'function' ){
let promise = onFulliled( self._param );
if( promise == undefined ){//说明首次
reject( self._param );
} else if ( promise.constructor == self.constructor ){
promise.then( function(param){
resolve( param );
}, function( param ){
reject( param );
})
} else {
reject( self._param );
}
} else {
reject( self._param );
}
}
}
}
此时,一个简单版的promise即完成。
特别感谢
看了很多个版本的promise,觉得该文的promise最好理解。即借鉴了思维,感谢他: segmentfault.com/a/119000001…
项目地址
完整代码请看git: