Promise
Promise对象,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
(2)Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
//执行的顺序 :new Promise 中的函数 ==> 当前队列中的同步代码 ==> then中的回调函数
const promise1 = new Promise(function (resolve, reject) { //异步代码,resolve成功,reject失败 二者之间只能执行一个
// ... some code
console.log("promise");
if ( /* 异步操作成功 */ ) {
resolve(value);
} else {
reject(error);
}
});
//第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。
promise1.then(function (value) {
// success
}, function (error) {
// failure
});
Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
异步加载图片的例子
function loadImageAsync(url) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function () {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
loadImageAsync("http://img4.imgtn.bdimg.com/it/u=2350302849,3323337377&fm=26&gp=0.jpg").then((img)=>{
box.appendChild(image);//显示在页面上
console.log("success");
}),(e)=>{
console.log(e);
}
Promise对象实现的 Ajax 操作的例子
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
getJSON方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。
另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获
如果 Promise 状态已经变成resolved,再抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
//Promise 的状态一旦改变,就永久保持该状态,不会再变了。
Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on
Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
let p1 = new Promise((resolve,reject)=>{
resolve("p1 ok");
});
let p2 = new Promise((resolve,reject)=>{
//reject("error"); //1.
//resolve("p2 ok"); //2.
});
let p3 = new Promise((resolve,reject)=>{
resolve("p3 ok");
});
let pAll = Promise.all([p1,p2,p3]);
pAll.then((res)=>{
console.log(res);//1. ["p1 ok", "p2 ok", "p3 ok"]
//如果其中一个变成reject,则最后为失败 2. error
}).catch((e)=>{
console.log(e);
})
如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
如果p2没有自己的catch方法,就会调用Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了
Promise.race()
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
const p = Promise.race([p1, p2, p3]);
let p1 = new Promise((resolve,reject)=>{
resolve("p1 ok");//首先进行
});
let p2 = new Promise((resolve,reject)=>{
reject("error");
});
let pAll = Promise.race([p1,p2]);
pAll.then((res)=>{
console.log(res);//p1 ok
}).catch((e)=>{
console.log(e);
})
let p1 = new Promise((resolve,reject)=>{
reject("error");
});
let p2 = new Promise((resolve,reject)=>{
resolve("p2 ok");
});
let pAll = Promise.race([p1,p2]);
pAll.then((res)=>{
console.log(res);//error
}).catch((e)=>{
console.log(e);
})
let p1 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("JS");
},2000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("ES6");
},1000);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(()=>{
reject("111");//最快进行
},100);
});
let pAll = Promise.race([p1, p2, p3]);
pAll.then((res) => {
console.log(res);//111
}).catch((e) => {
console.log(e);
})