18. Promise
Promise
18.1 Promise含义
promise是异步编程的一种解决方法。
所谓promise,简单说是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上说,promise是一个对象,从它可以获取异步操作的消息,promise提供了统一的API,各种异步操作都可以用同样的方法进行处理。
Promise对象代表一个异步操作,有着三种状态:
pending,正在进行状态
fulfilled,已成功状态
rejected,已失败状态
**注意:**只有异步操作的结果可以决定当前是哪一种状态,任何其它的操作都无法改变该状态
18.2 Promise对象的特点
(1)对象的状态不受外界影响,promise对象代表一个异步操作,有三种状态,pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是promise这个名字的由来“承若”;
(2)一旦状态改变就不会再变,任何时候都可以得到这个结果,promise对象的状态改变,只有两种可能:从pending变为fulfilled,从pending变为rejected。这时就称为resolved(已定型)。如果改变已经发生了,你再对promise对象添加回调函数,也会立即得到这个结果,这与事件(event)完全不同,事件的特点是:如果你错过了它,再去监听是得不到结果的。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
18.3 Promise的用法
Promise是一个构造函数,这个构造函数里有两个参数,分别是:resolve(成功之后的回调函数)、reject(失败之后的回调函数)。
因为promise表示的是一个异步操作,每当我们new一个promise实例,就表示一个具体的异步操作,那么这个异步操作的结果就只能有两种状态:成功/失败,两者都需要回调函数resolve/reject返回。所以内部拿到操作的结果后,无法使用return把操作结果返回给调用者,这时候只能用回调函数的形式来把成功或失败的结果返回给调用者。
var promise = new Promise( function (resolve, reject) {
if (true) {
resolve(value)
} else {
reject(error)
}
})
promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
promise.then( (value) => {
// success
}, (error) => {
// failure
})
then方法可以接受连个回调函数作为参数,第一个回调函数是promise对象的状态变为resolved时调用,第二个回调函数是promise对象的状态变为rejected时调用,其中,第二个函数是可选的,不一
定要提供,这两个函数都接受promise对象传出的值作为参数;
通过then指定回调函数的时候,成功的回调函数必须传,失败的回调函数可以胜利。
Promise可以作为一个构造函数对象,传入的两个参数都是函数,第一个函数是代表成功,第二个代表失败,每个函数调用后都会在then()中找到自己对于的函数来执行
var p=new Promise(function(success,rejected){
setTimeout(function(){
try{
console.log(123);
success(456);
}catch{
rejected("error");//在一次操作中之后进行success()函数和rejected()函数中的一个,执行
} //完毕后直接到下一个步骤
},1000)
}).then(function(data){//执行success()后进入该函数
console.log(data);//456
},function(err){//执行rejected()后进入该函数
console.log(err);//error
})
/*
then()函数中的第一个回调函数中的参数就是在前一个异步操作中通过success()传的值,而第二个回调函数中的参
数是在前一个异步操作中rejected()传的值
*/
//Promise正确的用法是实现链式操作来实现异步
var p=new Promise(function(success,rejected){
console.log(123);
success(456);
}).then(function(data){//假设现在只有成功才执行函数
console.log(data);//456
return 789;
/*
如果这里不写返回值,默认会返回undefined给下面then()中成功时的函数,注意的是这样写无论写什么普通的 数据类型都是成功的,都是为下面的then()函数传递了成功时的Promise
*/
}).then(function(data){
console.log(data);//789
})
//链式操作中失败的时候可以执行的异步操作
var p=new Promise(function(success,rejected){
try{
console.log(123);
success(456);
}catch{
rejected("error");
}
}).then(
function(data){
console.log(data);//456
return new Promise(function(success,rejected){
try{
success(789)
}catch{
rejected("error");
}
});
},
function(err){
console.log(err);//error
//失败就不会执行后面的then()了
}).then(
function(data){
console.log(data);//789
return new Promise(function(success,rejected){
rejected("error");//直接显示失败
});
},
function(err){
console.log(err);//error
}).catch(
function(err){
console.log(err);//error
})
/*
可以在最后加上catch()代表着只有在失败时才会进行的方法,该方法可以看作是then()失败时的简写,因为then()
要写失败时的情况必须要传入两个数,第二个才是失败时的参数函数,用catch()可以值捕获失败时的结果
*/
18.4 异常捕获
then() 和 .catch()
Promise构造器接受一个函数作为参数,这个函数有两个参数:resolve,reject,分别代表这个Promise实例成功之后的回调函数和失败之后的回调函数。
举个例子:
var promise = new Promise(function (resolve, reject) {
var a = 1
if (a == 1) {
resolve(a)
} else {
reject(error)
}
})
promise.then(function (value) {
console.log(value);
}).catch(function (error) {
console.log(error);
})
// 输出:
// 1
不管是then方法还是catch方法返回的都是一个新的Promise实例,这意味着Promise可以链式调用then和catch,每一个方法的返回值作为下一个方法的参数:
var promise = new Promise(function (resolve, reject) {
var a = 1
if (a === 1) {
resolve(a)
} else {
reject(error)
}
})
promise.then(function (value) {
console.log(value++)
return value
}).catch(function (error) {
console.log(error)
}).then(function (value) {
console.log(value++)
})
// 输出:
// 1
// 2
如果其中一个then失败了,它后面第一个catch方法就会接受这个错误并执行,然后继续执行后面的方法,比如:
var promise = new Promise(function (resolve, reject) {
resolve()
})
promise.then(function (value) { // 抛出错误
console.log(1 / x)
}).then(function () { // 此方法不会执行
console.log('This "then" method will not happend!')
}).catch(function (error) { // 接受错误,并打印出错误
console.log('Have an error: ',error)
}).then(function () { // 此方法会执行
console.log('This "then" method will happend!')
})
// 输出:
/*
Have an error: ReferenceError: x is not defined
at /Users/linxueying/Library/Mobile
......
This "then" method will happend!
*/
18.5 静态方法
18.5.1 Promise.all()
Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例
var p1=new Promise(function(){});
var p2=new Promise(function(){});
var p3=new Promise(function(){});
var p=Promise.all([p1,p2,p3]).then(function(data){
console.log(data);
},function(err){
console.log(err);
});
/*
p内部的状态由p1,p2,p3决定,有两种情况:
1.只有p1,p2,p3全部成功时p的状态才能使成功,p才能够执行后面成功的回调函数,此时data的值是由 p1,p2,p3共同传入的值组成的数组
2.只要p1,p2,p3中有一个失败,p的状态就会变成失败,p执行失败后的回调函数,此时err的值为首先完成异步并 返回失败的Promise的返回值
*/
注意:
Promise,all()中包含多个Promise实例的数组,只有这多个实例全部成功,或者至少有一个失败时才会调用后面then()中的回调函数
如果作为参数的Promise实例自己定义了catch()方法,那么该实例一旦失败不会触发Promise.all()后面定义的catch()方法,如果没有实例定义catch()方法,那么触发失败后会执行Promise.all()后面的catch()方法
18.5.2 Promise.race()
Promise.race()方法也用于将多个Promise实例包装成一个新的Promise实例
var p1=new Promise(function(){});
var p2=new Promise(function(){});
var p3=new Promise(function(){});
var p=Promise.all([p1,p2,p3]).then(function(data){
console.log(data);
},function(err){
console.log(err);
});
/*
p内部的状态由p1,p2,p3其中一个决定,只有其中有一个先完成异步操作,p的状态就会跟着发生改变,如果先发生 成功则执行第一个回调函数,如果先发生失败则执行第二个回调函数
*/
18.5.3 Promise.resolve()
Promise.resolve()方法用于将现有的对象转换为Promise对象,并且该对象中的值就是成功时的函数传入的参数
var p=Promise.resolve(123);
//等价于
var p=new Promise(success,rejected){
success(123);
}
该方法的参数有多种情况
参数为一个Promise实例 不作任何改变,直接返回这个Promise实例
参数为一个具有then()方法的thenable对象
Prommise.resolve()方法会将这个对象转换为Promise对象,然后立即执行里面的then()方法
var thenable={ then:function(success,rejected){ console.log(123);//123 success(456) } }; var p = Promise.resolve(thenable).then(function(data) { console.log(data);//456 }); /* 上面的代码会在控制台上打印出来 */
参数为一个普通值类型的或者不是thenable对象时 会直接将传入的参数作为成功时的参数传入到then()的成功函数的参数中
没有参数 直接得到一个成功时不传入参数的Promise对象
18.5.4 Promise.reject()
Promise.reject()方法也用于将现有的对象转换为Promise对象,并且该对象中的值就是失败时的函数传入的参数
**注意:**Promise.reject()方法中传入的参数会原封不动的作为失败时的错误理由,不会发生参数的改变,和Promise.resolve()方法有区别
var p=Promise.reject("error");
//等同于
var p=new Promise(function(success,rejected){
rejected("error");
})
18.6 Promise优缺点
优点 Promise的状态不会受到外界的影响,并且一旦Promise的状态发生改变时,就不会再进行状态变化,任何时刻都可以得到这个状态结果
缺点
- 无法取消Promise,一旦新建就会立即执行,无法在中途取消
- 如果不设置回调函数,Promise的内部机制会报错,不会反应到外部
- 当处于pending正在执行的状态时,不能知道当前在哪一个阶段,不知道是否是开始还是结束