Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。
先来一个例子 A 熟悉下语法
var p = new Promise(function(resolve, reject){
console.log('1');
// 运行后续的 callback 列表,例如:then,when等。否则,不会执行then里面的函数
resolve('go next');
});
// 只考虑成功
p.then(function(){
console.log(2, arguments);
return 'next';
}, null).then(function(){
console.log(3, arguments)
}, null);
// 输出
1
2 ['go next']
3 ['next']
最初的设想
function Promise(func){ // 接收一个函数作为参数
}
Promise.prototype = {
then: function(callback){ // 接收函数作为参数
return this; // 支持链式调用
}
}
观察例 A,resolve是一个函数,并且不是用户传的,所有Promise自身应该有一个resolve方法,并且这个方法要传递给Promise构造器里面的函数作为参数。
function Promise(func){ // 接收一个函数作为参数
**func(this.resolve);**
}
Promise.prototype = {
**resolve: function(){
}**,
then: function(callback){ // 接收函数作为参数
return this; // 支持链式调用
}
}
Promise是按照顺序来执行callback的,并且由resolve触发。
function Promise(func){ // 接收一个函数作为参数
**this.doneList = []; // callback 列表**
**func(this.resolve.bind(this)); // 顺带绑定this对象**
}
Promise.prototype = {
resolve: function(){
**//执行回调列表
while(true){
if( this.doneList.length === 0 ){
break;
}
this.doneList.shift().apply(this);
}**
},
then: function(callback){ // 接收函数作为参数
**this.doneList.push(callback); // 加入到回调队列**
return this; // 支持链式调用
}
}
好,现在写一个测试用例
var p =new Promise(function(resolve){
console.log('a');
resolve();
}).then(function(){
console.log('b');
});
// 输出
'a'
什么鬼?打印下p
console.log(p);
我们发现原来doneList里面还有一个函数没有运行,再运行下这个函数
p.doneList[0].call(this);
//结果
console.log('b'); // 打印 b
打断点跟踪,发现Promise先执行resolve方法,然后执行then,把函数push到doneList。但是,再也没有执行过doneList里面的函数了。怎么办呢?我们可以给Promise加一个属性(state)来描述当前状态,分为【未完成】(pending)和【完成】(done)。如果执行then时,状态已经是完成态,那么切换到未完成态,并执行resolve方法。
function Promise(func){ // 接收一个函数作为参数
**this.state = 'pending'; // 初始化状态**
this.doneList = []; // callback 列表
func(this.resolve.bind(this)); // 顺带绑定this对象
}
Promise.prototype = {
resolve: function(){
//执行回调列表
while(true){
if( this.doneList.length === 0 ){
**this.state = 'done'; // 回调列表为空,改变状态**
break;
}
this.doneList.shift().apply(this);
}
},
then: function(callback){ // 也是接收函数
this.doneList.push(callback); // 加入到回调队列
if( this.state === 'done'){
this.state = 'pneding';
this.resolve();
}
return this; // 支持链式调用
}
}
用和上面类似的例子再次测试
var p =new Promise(function(resolve){
console.log('a');
resolve();
}).then(function(){
console.log('b');
}).then(function(){
console.log('c');
});
结果截图
这下,我们自定义的Promise基础功能完成了最核心的部分了。也许有人会疑问,你这写的什么鬼?下面的代码更简单,也能实现相同的功能
function Promise(func){ // 接收一个函数作为参数
func(this.resolve.bind(this)); // 顺带绑定this对象
}
Promise.prototype = {
resolve: function(){
//什么也不干
},
then: function(callback){ // 也是接收函数
callback.call(this); // 直接运行
return this; // 支持链式调用
}
}
测试用例
var p =new Promise(function(resolve){
console.log('d');
resolve();
}).then(function(){
console.log('e');
}).then(function(){
console.log('f');
});
结果:
但是,文章最前面说过
Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算
并且会顺序执行回调列表(doneList)。最终代码及测试
function Promise(func){
this.state = 'pending';
this.doneList = [];
func(this.resolve.bind(this));
}
Promise.prototype = {
resolve: function(){
while(true){
if( this.doneList.length === 0 ){
this.state = 'done';
break;
}
this.doneList.shift().apply(this);
}
},
then: function(callback){
this.doneList.push(callback);
if( this.state === 'done'){
this.state = 'pending';
this.resolve();
}
return this;
}
}
var p = new Promise(function(resolve){
window.setTimeout(function(){
console.log('d');
resolve();
}, 1000);
}).then(function(){
console.log('e');
}).then(function(){
console.log('f');
});
console.log('被阻塞了吗?');
输出:
先输出'被阻塞了吗?',一秒钟后相继输出 d、e、f 。可以看出,不但没有阻塞后面的代码执行,而且回调也是按照顺序执行的。
结束。
下篇继续完善,例如数据传递以及then中函数返回一个Promise时,如何处理。欢迎大家有疑问或者建议,一起来交流。