javascript的Promis对象
背景
JavaScript作为单线程运行于浏览器之中,这是每本JavaScript教科书中都会被提到的。在浏览器中的大多数任务都是异步(无阻塞)执行的,例如:鼠标点击事件、窗口大小拖拉事件、定时器触发事件、Ajax完成回调事件等。当每一个异步事件完成时,它都将被放入一个叫做”浏览器事件队列“中的事件池中去。而这些被放在事件池中的任务,将会被javascript引擎单线程处理的一个一个的处理,当在此次处理中再次遇见的异步任务,它们也会被放到事件池中去,等待下一次的tick被处理。由于浏览器的这种内部事件循环机制,所以JavaScript一直以callback回调的方式来处理事件任务。因此无所避免的对于多个的JavaScript异步任务的处理,将会遇见”callback hell“(回调地狱),使得这类代码及其不可读和难易维护。
看下面代码:
asyncData1(data, function (data1){
asyncTData2(data1, function (data2){
asyncData3(data2, function (data3){
// .... 魔鬼式的金字塔还在继续
});
});
});
Promise示例
var promise = new Promise(function(resolve, reject){
//do some asyn things
setTimeout(function(){
resolve({
name : 'jack',
age : 22
});
},1000)
});
function fulFillFunc(data){
console.log("fuldata2="+data);
}
function rejectFunc(data){
console.log("rejdata="+data);
}
promise.then(fulFillFunc,rejectFunc);
这就是promis,promis在英语中的意思是承诺,跟咱们程序猿一样,很专一。
promis的状态
promis一共有三个状态:pending、Fulfilled、Rejected;pending是promis对象的初始状态,等到异步任务完成或者被拒绝的时候,状态发生转变。任务完成状态的转变是从pending转到Fulfilled;任务被拒绝时状态的转变是从pending转到Rejected,不吃回头草(不可逆向转变),也不这胸望着那胸高(fulfilled和rejected两种状态不可相互转换)。
Promise构造函数
Promise的构造函数接收一个必填的参数,且该参数为函数,这个参数函数还包含两个函数参数,第一个resolve是触发Promise对象状态从pending转变为fulfilled的函数,第二个reject是触发Promise对象状态从pending转变为rejected的函数。当执行了resolve函数的时候,Promise对象状态就转换成了fulfilled;当执行了reject函数,Promise对象的状态就转换成了rejected。
then
Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致;then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由“Pending”状态转换到“Fulfilled”状态时被调用和在Promise由“Pending”状态转换到“Rejected”状态时被调用。
多个任务的串行执行
在上文中提到的回调地狱案例,就是一种试图去将多个异步的任务串行处理的结果,使得代码不断的横向延伸,可读性和维护性急剧下降。当然我们也提到了Promise利用链式和延迟执行模型,将代码从横向延伸拉回了纵向增长。使用Angular中$http的实现如下:
$http.get('/data1')
.then(function(data1){
//dosomething
console.log('data1', data1);
return $http.get('/data2', {params: data1.result});
})
.then(function(data2){
//dosomething
console.log('data2', data2);
return $http.get('/data3', {params: data2.result});
})
.then(function(data3){
//dosomething
console.log('data3', data3.result);
});
多个任务的并行执行
在有些场景下,我们所要处理的多个异步任务之间并没有像上例中的那么强的依赖关系,只需要在这一系列的异步任务全部完成的时候执行一些特定逻辑。这个时候为了性能的考虑等,我们不需要将它们都串行起来执行,并行执行它们将是一个最优的选择。如果仍然采用回调函数,则这是一个非常恼人的问题。利用Promise则同样可以优雅的解决它:
var promise1 = new Promise(function(resolve, reject){
//do some asyn things
setTimeout(function(){
resolve({
name : 'jack',
age : 22
});
},1000);
});
var promise2 = new Promise(function(resolve, reject){
//do some asyn things
setTimeout(function(){
resolve({
name : 'wen',
age : 23
});
},1000);
});
var promise3 = Promise.resolve(110)
Promise.all([promise1,promise2,promise3]).then(function(data){
console.log(data[0]);
console.log(data[1]);
console.log(data[2]);
},function(){
console.log("rejected");
})