promise对象的两个特点:
- 对象的状态不受外界影响。promise对象有三种状态:Pending(进行中),Resolved(已完成,也叫fulfilled)和rejected(已失败)。
- 一旦状态改变,就不会再变。
构造promise函数:
var promise = new Promise(function(resolve, reject){
if (/* 异步操作成功*/) {
resolve(value);
} else {
reject(error);
}
});
其中resolve函数的作用:将promise的状态从“未完成”变成“成功”,pending -> resolved
reject函数作用:将promise状态从pending -> rejected
.then的使用
1) 构造promise函数后,可以使用.then来指定对应的resolve和reject函数。
promise.then(function(value){
// success
}, function(error){
// failure
})
2)resolve的执行顺序问题
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
以上代码运行结果为:先打印2,再打印1。这是因为resolve是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
new Promise((resolve, reject) => {
reject('error');
console.log(2);
}).catch(r => {
console.log(r);
});
同理,对于reject也是一样,运行结果为先打印2,再打印error
这里面牵涉到一个事件循环的原理。为了更好的理解,插播一下事件循环的原理:
众所周知,JS是单线程。为了解决JS单线程漫长等待的问题,产生了两种JS的执行方式,即同步执行和异步执行。同步执行是指,一个任务结束了然后再执行下一个任务,异步执行是指CPU跳过等待时间长的任务,先去执行后面的任务。
异步执行的机制实现如下:
1 所有任务都在主线程上执行,形成一个执行栈。
2 主线程之外,有一个任务队列。系统会把异步任务防在任务队列里,然后继续去执行后续的任务。
3 执行栈中的任务执行完毕,系统就会去任务队列里读取异步任务,如果此时异步任务已经结束了等待状态,就会从任务队列进入执行栈。
4 主线程不断重复以上过程,就是事件循环的过程。
5 定时器setTimeout和setInterval也会将事件插入到任务队列中,也就是必须要等到当前代码执行执行完,主线程才会去执行指定的回调函数。所以没有办法保证,回调函数一定会在指定的时间内执行。
6 DOM的变动,通常不会立即执行,而是16毫秒执行一次,使用requestAnimationFrame()的效果要好于setTimeout()。
3)链式的then
采用链式的then,可以指定一组按照次序调用的回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入下一个回调函数中。
.catch的使用
Promise.prototype.catch方法是.then(null, reject)的别名,用于指定发生错误时的回调函数。主要有两个用户:在异步操作抛出错误状态变成rejected的时候调用.catch指定的回调函数,在then方法指定的回调函数执行发生错误时会被.catch方法捕获。
使用.catch捕获错误如下:
var promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
运行结果为:
Error: test error
at <anonymous>:2:9
at Promise (<anonymous>)
at <anonymous>:1:15
等同于:
var promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
promise.all()
用于将多个promise实例,包装成一个新的promise实例。
var p = Promise.all([p1, p2, p3]);
P的状态由p1,p2,p3决定:
1 p1,p2,p3的状态都变成fulfilled,p的状态才会变成fulfilled。
2 只要有一个返回的状态是rejected,那么p的状态就变成rejected,且第一个被rejected的实例的返回值,会传递给p的回调函数。注意,如果实例定义了自己的catch方法,则不会触发promise.all()的catch方法。
var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve("Hello");
}, 3000);
});
var p2 = new Promise(function (resolve) {
setTimeout(function () {
resolve("World");
}, 1000);
});
Promise.all([p1, p2]).then(function (result) {
console.log(result);
});
上述运行结果为: ["Hello", "World"],因为Promise.all方法会按照数组里面的顺序返回结果。
promise.race()
var p = Promise.race([p1, p2, p3]);
也是将多个promise实例包装成一个新的promise实例。与上面的方法不同的是,只要p1p2p3有一个实例率先改变了状态,p的状态也跟着改变,而且率先改变的promise的返回值,会传给P的回调函数。