一.我们为什么要用Promise
javaScript处理异步都是由callback的形式执行,由js宿主环境(浏览器,node)为一些耗时任务开辟另外的线程。这种callback机制深入人心,但是它的写法会带来一些不便,我们拿ajax举例:
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
//业务代码
}
}
})
}
})
这里举了一个极端点的例子,这个方法套了三层ajax,乍一看还是很迷糊的。但是如果同样的事交给Promise做,就清晰很多了。
new Promise(function(resolve,reject){
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
resolve(data);
})
}).then(function(data){
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
return Promise.resolve(data);
}
})
}).then(function(){
$.ajax({
type:'post',url:'xxx.do',
data:{},
success:function(data){
//业务代码
}
})
})
有没有发现原来层层嵌套的ajax变成平铺排列了,就好像原来是把一块一块的砖垒起来,现在是将他们平铺开了。这样不仅看起来更清晰,也将异步操作以同步的形式展现出来。接下来我们看看Promise的语法规范。
二.语法规范
1.概念
Promise 对象有三种状态: Pending – Promise对象的初始状态,等到任务的完成或者被拒绝;Resolved – 任务执行完成并且成功的状态;Rejected – 任务执行完成并且失败的状态;
Promise的状态只可能从Pending状态转到Resolved状态或者Rejected状态,而且不能逆向转换,同时Resolved状态和Rejected状态也不能相互转换;
Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致。这一点很重要,我觉得这个是promise被应用的主要原因,如果没有then的链式调用,跟普通callback比,没啥优势。
then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由Pending状态转换到Resolved状态时被调用和在Promise由Pending状态转换到Rejected状态时被调用。默认then里面第一个函数是resolve的回调,第二个是reject的回调。
2.API
Promise.resolve() 执行成功
Promise.reject() 执行失败
Promise.prototype.then() 递延处理
Promise.prototype.catch() 异常
Promise.all() 所有的完成 Promise.all方法常被用于处理多个promise对象的状态集合
三.实例
先举一个小例子:
var key=false;
new Promise(function(resolve, reject){
if(key){
resolve(123)//调用resolve,表示执行成功
}else{
reject(321)//调用reject,表示执行失败
}
}).then(function(value) {
//执行成功的回调函数
console.log(value); // key=true 123
}, function(reason) {
//执行失败的回调函
console.log(reason); //key=false 321
})
再举一个例子体现then回调的异步性
javascript] view plain copy
var p = new Promise(function(resolve, reject){
resolve("success");
});
p.then(function(value){
console.log(value);
});
console.log("first");
//"first"
//"success"
事实证明then的回调也是浏览器分配的线程,与主线程异步,任务放在callbackqueue里排队。
再举一个链式调用的例子:
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){
//执行成功,接收由resolve(1)传入的回调函数参数 1,即value为1
console.log(value);
return value*2; //返回 2,作为 执行成功回调函数的参数
}).then(function(value){
//接收到回调函数的参数 value=2
console.log(value); //没有返回值,执行成功 的回调函数没有传入参数
}).then(function(value){
console.log(value);
return Promise.resolve('resolve');
//调用resolve方法表示执行成功,并传入 'resolve'字符串作为 执行成功回调函数的参数,这里也必须用return,不用return接下来会执行resolve的回调,没有参数,执行完resolve回调后会抛出异常
}).then(function(value){
console.log(value);
return Promise.reject('reject'); //表示执行失败,调用执行失败的回调函数
//并传入参数reject
},
function(value){
console.log(value);
return Promise.reject('reject');
}).then(function(value){
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err); //执行了失败的回调,接收到参数 'reject'
})
执行结果如图:
四.补充
catch()的作用
1.他可以和then的第二个参数一样,用来制定reject的回调。
2.当执行resolve的回调,如果代码出错抛出异常时,也不会报错卡死js,会进入到catch里面
3.添加多个catch,精准捕获异常。