Promise对象
Promise是异步编程的一种解决方法,比传统的解决方案——回调函数和事件,更合理更强大!
所谓Promise,就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上说,Promise是一个对象,它可以获取异步造作的消息,Promise提供统一的API,各种异步操作都可以用同样的方法进行处理
特点
-
对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪种状态,任何其他操作无法改变这个状态。
-
一旦状态改变,就不会再变,任何时候都可以得到这种结果,Promise对象的状态改变之后两种可能:从pending变为fulfilled和pending变为rejected,只要这两种情况发生,状态就回凝固,不会再变,会一直保持这个结果。如果状态已经发生,再对Promise对象添加回调函数,也会立即得到这个结果,这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例
const promise = new Promise(function(resolve,reject){
// some colde
if(/* 异步操作成功 */){
resolve()
}else{
reject()
}
})
Promise构造函数接受一个函数作为参数,该函数的两个参数resolve和reject,它们是两个函数,由JavaScript引擎提供,不用自己部署
resolve函数作用:将Promise对象的状态从‘pending’变成‘resolved’,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数作用:将Promise对象的状态从‘pending’变成‘rejected’,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
//then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用
promise.then(function(value){
//success
}),function(error){//第二个回调函数是Promise对象的状态变为rejected时调用(可选)
//failure
}
//两个函数都接受Promise对象传出的值作为参数
举个🌰
//timeout方法返回一个Promise实例,表示一段时间后才会发生的结果
//过了指定时间(ms参数)以后,Promise实例的状态变为resolved,就回触发then方法绑定的回调函数
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done')
})
}
timeout(3000).then((value) => {
console.log(value); //done
})
Promise新建后就会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('promise');
resolve();
})
promise.then(function() {
console.log('resolved');
})
console.log('Hello');
//promise
//Hello
//resolved
用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);
});
上面代码中,getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数调用时,都带有参数
如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数,reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以为,还有可能是另一个Promise实例,例如:
const p1 = new Promise(function(resolve, reject) {
//...
})
const p2 = new Promise(function(resolve, reject) {
//...
resolve(p1);
})
//p1和p2都是Promise实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作
注意:这时p1的状态就会传递给p2,也就是说p1的状态决定了p2的状态,如果p1的状态是pending,那么p2的回调函数就回等待p1的状态改变,如果p1的状态已经是resolved或者rejected,那么,p2的回调函数将会立即执行。
const p1 = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function(resolve, reject) {
setTimeout(() => resolve(p1), 1000);
})
p2.then(result => console.log(result)).catch(error => console.log(error)); //Error: fail
上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
注意:调用resolve或reject并不会终结Promise的参数函数的执行
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
})
//2
//1
上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来,这时因为立即resolved的Promise是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve或reject以后,Promise的使命就完成了,后续操作应该放到then方法里面,而不应该直接写在resolve或reject的后面,所以,最好在它们前面加上return语句,这样就不会有意外
new Promise((resolve, reject) => {
return resolve(1);
//不会执行后面操作
console.log(2);
}).then(r => {
console.log(r);
})
//1