上一篇讲了生成器函数,本篇简单的分析一下Promise对象。
Promise 是一个 ECMAScript 6 提供的类对象,目的是更加优雅地书写复杂的异步任务,传统的方法是执行层层回调函数,缩进和结构混乱。
1.Promises的基本用法:
Promise通过构造函数来声明, 此构造函数只有一个参数,是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve(解决) 和 reject(拒绝),而这两个参数也是函数,其结果返回到then方法的两个函数的参数中,调用方式如下:
let p=new Promise(function(resolve,reject){
setTimeout(()=>{
if(Math.random()>0.5){
resolve("好好学习天天向上");//承诺成功
}else{
reject("休息会");//承诺失败
}
},3000)
});
//p代表的是promise实例
p.then(function(data){//成功的回调函数
console.log(data);
},function(err){//失败的回调函数
console.log(err);
})
上面代码,resolve方法的结果传递给then第一个函数参数data,reject方法的结果传递给then第二个函数参数err。
另外Promise类,resolve和reject方法谁先执行了,Promise的状态就是谁的状态,后面的方法对于Promise的状态没有影响,resolve先执行,那么就传递给then回调函数bin执行,如果reject先执行,那么就传递给catch回调函数并执行.
2.Promises的.catch() 和 .finally()方法
Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列,无论是否异常最后都会执行。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列,如果采用采用.catch()方法,那么.then()方法只需要一个函数参数即可,上面的代码改成如下:
let p=new Promise(function(resolve,reject){
setTimeout(()=>{
if(Math.random()>0.5){
resolve("好好学习天天向上");//承诺成功
}else{
reject("休息会");//承诺失败
}
},3000)
});
//p代表的是promise实例
p.then(function(data){//成功的回调函数
console.log(data);
}).catch(function (err){
console.log(err);
}).finally(function () {
console.log("End");
});
3.Promises的链式调用
resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作:
let t= new Promise(function (resolve, reject) {
console.log(1111);
resolve(2222);
});
t.then(function (value) {
console.log(value);
if(Math.random()>0.5){
throw "Overflow";
}
return 3333;
}).then(function (value) {
console.log(value);
throw "An error";
}).catch(function (err) {
console.log(err);
});
上述链式调用中,第一个then方法的return返回值作为第二个then方法的输入参数,链式调用中只要一个环节异常,那就中断跳过接下来的then调用而执行catch方法,有趣的是Promise对象链式执行then catch方法时是只需一个函数参数,如果这种一个参数独立调用时会报错。比如如果写成t.then()和t.catch()两个执行语句,当Promise返回的是reject函数的值时,就报错。
如果then方法返回的是 Promise对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,比如如下的操作:
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});
4.等待Promises多个调用
对于等待多个独立的异步任务,可以用Promise.all来简化代码,Promise.all方法用于传入多个 Promise 实例,这个方法将一个Promise数组作为参数,然后创建一个新的Promise对象,只有数组中的Promise全部被解决,这个返回的Promise就会被解决执行then的resolve方法,而一旦其中有一个Promise失败了,那么整个心Promise对象就会被拒绝,执行catch或者reject方法。比如:
let p=new Promise(function(resolve,reject){
setTimeout(function(){
resolve(1);
},10000)
})
let p1=new Promise(function(resolve,reject){
setTimeout(function(){
resolve(2);
},5000)
})
let p2=new Promise(function(resolve,reject){
setTimeout(function(){
resolve(3);
},2000)
})
Promise.all([p,p1,p2]).then(function(value){
console.log(value);//[1,2,3]
},function(reason){
console.log(reason);
});
此任务组3个任务并行执行,所以等待时间按照最大的p计算为10秒,任务组的返回值是3个任务返回值组成的数组,3个任务有一个错误就执行 console.log(reason),只有3个都正确解决了,才会执行console.log(value)。
5.Promise.race()多个调用竞争
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.race方法的参数与Promise.all方法一样,但是执行的时候是那个任务先完成,返回那个任务的resolve和reject结果,示例代码如下:
var p1 = new Promise(function(resolve, reject) {
setTimeout(function () {
console.log(1);
resolve('one');
}, 2000);
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(function () {
console.log(2);
resolve('two');
}, 1000);
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// 两个都完成,但 p2 更快
});
执行结果为:
2
two
1