最近看了一篇关于Promise
内部实现原理的文章Javascript in wicked detail。做者从简明的例子入手,一步一步的构建健壮的Promise
实现。我就拿做者文中的代码实例梳理下文章的核心内容。javascript
你们必定看到过嵌套很深回调函数,那么如何在保证代码流程才能将这些纵向嵌套的代码变成横向偏平的呢?java
doSomething(function(value) { console.log('Got a value:' + value); })
to thispromise
doSomething().then(function(value) { console.log('Got a value:' + value); })
那么咱们就应该在定义doSomething
函数的时候作出相应的变化异步
function doSomething(callback) { var value = 42; callback(value); }
to this函数
function doSomething() { return { then: function(callback) { var value = 42; callback(42); } } }
首先来看一段定义简单Promise
构造函数的代码:this
function Promise(fn) { var callback = null; this.then = function(cb) { callback = cb; } function resolve(value) { callback(value) } fn(resolve); }
而后重写doSomething()
函数:code
function doSomething() { return new Promise(function(resolve) { var value = 42; resolve(value); }) }
从新定义后的doSomething()
函数执行后返回获得一个promise实例
,实例上有then()
方法,能够接受回调函数。对象
doSomething().then(function(value) { console.log(value); })
可是上面的代码会报错(callback is undefined)
,是由于:resolve中的callback要早于then()方法中的callback的赋值操做。队列
那么对Promise
构造函数稍微处理下,把同步的代码使用setTimeout
来hack下,改变代码的执行顺序,使得resolve
函数中的callback
对value
进行处理前被赋值了。ip
function Promise(fn) { var callback = null; this.then = function(cb) { callback = cb; } function resolve(value) { setTimeout(function() { callback(value); }, 1) } fn(resolve); }
这里经过setTimeout
异步函数改变了代码执行的顺序,确保callback
被调用前已经被赋值成cb
。
从新调用:
doSomething().then(function(value) { console.log(value); }) // 42 //正常执行。
可是定义Promise
构造函数的代码仍是有问题的,由于若是仅仅是调用then()
方法而注入回调的话,内部的callback
仍然是null
。一样不能正常的执行。
别急,慢慢来。
事实上Promise
是有状态的:
pending
resolved
rejected
pending => resolved
或者 pending => rejected
。状态一旦发生改变,不可逆。接下来,让咱们在Promise
的构造函数里面加入state
,使用state
来控制整个代码流程。
function Promise(fn) { var state = 'pending', value, deferred; function resolve(newValue) { state = 'resolved'; value = newValue; if(deferred) { handle(deferred); } } function handle(onResolved) { if(state === 'pending') { deferred = onResolved; return ; } onResolved(value); } this.then = function(onResolved) { handle(onResolved); } fn(resolve); }
代码变的比以前更加复杂。可是如今使用state
来控制代码的流程。then()方法
和resolve()方法
将控制权交给了新的方法handle()
,由handle()
方法来根据state
的值进行流程操做:
若是state
为pending
状态,即在resolve()
以前调用了then()
方法,那么会将onResolved
回调赋值给一个deferred延迟对象
,deferred对象
将这个回调保存起来,稍后当resolve()
调用时,pending
状态变为resolved
,并调用deferred对象
。
若是在then()
方法前调用resolve()
方法,pending
状态变为resolved
,而后调用then()
里面注入的回调onResolved
.
经过以上的代码,promise
能够任意次数的调用then()
方法:
var promise = doSomething(); promise.then(function(value) { console.log('Got a value:', value); }); // 42 promise.then(function(value) { console.log('Got the some value again:', value); }); //42
可是这样的Promis
e构造函数仍是有问题的,你们能够想象下,在调用resolve()
方法前,调用了不少次的then()
方法,那么只有最后一个then()
方法里面注入的callback
才会有用。解决这个问题的方法就是维持一个deferreds队列
,去保存每次then()
方法注入的回调函数。
下面的代码是最普通不过的promise链式调用:
getSomeData() .then(filterTheData) .then(processTheData) .then(displayTheData)
getSomeData()
方法调用后会返回一个promise对象
,这样即可以调用then()
方法,一样这第一个then()
方法调用后也会返回一个promise
对象。这样才能继续调用then()
方法。
then()方法老是返回一个promise。
接下来在代码中加以实现:
function Promise(fn) { var state = 'pending', value, deferred = null; function resolve(newValue) { state = 'resolved'; value = newValue; if(deferred) { handle(deferred); } } function handle(handler) { if(state == 'pending') { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); } this.then = function(onResolved) { return new Promise(function(resolve) { handle({ onResolved: onResolved, resolve: resolve }); }); }; fn(resolve); }
在此次的代码中,调用then()
方法后会返回一个新的生成的promise对象
。它具备then()
方法,能够继续调用then()
,并返回一个新生成的promise
对象。如此继续进行下去。这就实现了Promise
链式调用。
再来看看具体的代码实现:resolve()
方法没什么变化,可是handle()
方法接收一个handler
对象。handler
对象有2个属性,一个为onResolved
,then()
方法里面注入的回调函数,用来对传入的上一个promise
传递过来的值进行处理;另外一个为resolve
,Promise
构造函数内部定义的resolve()
方法,用来改变Promise
状态以及value
值。
具体分析下handle()
函数:
function handle(handler) { if(state === 'pending') { deferred = handler; return; } if(!handler.onResolved) { handler.resolve(value); return; } var ret = handler.onResolved(value); handler.resolve(ret); }
每次调用then()
方法新建一个promise
对象过程中,handle({onResolved: onResolved, resolve: resolve})
中resolve
属性始终是得到的定义过程当中对外部resolve
方法的引用。即上一次的promise
中定义的resolve
.
当then()方法里面注入回调函数时,调用onResolved方法并得到返回值ret,传入resolve方法,改变state的值以及更改promise中须要继续传递下去的值。若是onResolved方法中会返回处理过的值,那么下一个promise能拿到这个值,若是onResolved没有返回,传入下一个promise的为undefined**
doSomething().then(function(result) { console.log('First result', result); return 88; }).then(function(secondResult) { console.log('second result', secondResult); }) //the output is // //First result 42 //Second result 88 doSomething().then(function(result) { console.log('First result', result); }).then(function(secondResult) { console.log('Second result', secondResult); }) //now the output is //First result 42 //Second result undefined
当then()没有注入回调函数时,仍然会调用resolve方法,改变state的值,以及获取上一个promise传递过来的值,并将值传递给下一个promise。
doSomething().then().then(function(result) { console.log('Got a result', result); }); //the output is // //Got a result 42
主要是得益于handle()
方法中,调用resolve
方法获取从上一个promise
获得的value
以及做为传入下一个promise
的value
:
if(!handler.onResolved) { handler.resolve(value); return; }
再每次调用then()方法的过程都会新建一个pending状态的promise,并经过resolve方法改变状态,若是then()方法中注入了回调函数,并返回了值,那么这个值会一直传递下去,若是没有注入回调函数,resolve方法会获取上一个promise传递过来的值,并做为传入下一个promise的值。即then()方法注入的回调函数是可选的。
经过再次对Promise构造函数
的增强,完成了promise链式调用
的功能。
对于reject的部分过2天加上。