1. Promise是什么 ?
Promise是异步编程的一种解决方案。ES6将其写进了语言标准,比回调函数和事件更强大更合理。
Promise就是一个容器,保存着未来才会结束的事件(异步操作)的结果。有以下2个特点:
- (1). 对象状态不受外界影响。pending、fulfilled、rejected。只有异步操作的结果可以决定当前是哪一种状态,任何其他手段都无法改变;
- (2). 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise状态改变只有2种可能:pending->fulfilled 、pending->rejected.只要这两种情况发生,状态凝固,不会再变,这时就成为resolved(已定型)。resolved一般也指fulfilled状态,不包含rejected状态。
优点:
- 将异步操作以同步操作的流程表达,避免层层嵌套;
- promise对象对外提供统一接口,使得控制异步操作更容易。
缺点:
- 一旦执行,无法取消promise;
- 如果不设置回调函数,promise内部抛出的错误,无法反应到外部;
- pending时候,无法得知目前进展到哪一个阶段(刚开始还是即将完成);
2. 基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject){
if(异步操作成功){
resolve(value)
} else {
reject(error)
}
})
Promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用是,将promise从pending变成fulfilled,并将异步操作的结果,作为参数传递出去。reject同理。
promise实例生成完成之后,可以用then方法分别指定resolved状态和rejected状态的回调函数。这两个回调函数都接受promise对象传出的值作为参数。
Promise新建后就会立即执行。
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// 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);
});
resolve函数的参数除了正常的值之外,还可能是另一个Promise实例。
var p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject('fail'), 3000)
})
var p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2.then(function(a){
console.log(a);
},function(b){
console.log('b')
console.log(b);
}).catch(error => {
console.log('error')
console.log(error)
})
p1.then(function(a){
console.log(a);
},function(b){
console.log('bv')
console.log(b);
}).catch(error => console.log(error))
// 输出结果:
// b
// TypeError: Cannot read property 'push' of undefined
// at Function.then (public_dc35c87.js:779)
// at public_dc35c87.js:777
// at MutationObserver.a (public_dc35c87.js:545)
//bv
//fail
上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
所以一般,可以在resolve或reject前面加上return语句。
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
3. Promise.prototype.then()
Promise实例具有then方法,定义在原型对象伤。作用:为Promise实例添加状态改变时的回调函数。then方法第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。then方法返回的是一个新的Promise实例,所以可以链式调用。前一个then方法回调函数完成后,会将返回结果作为参数,传给后一个then回调函数。采用箭头函数书写更简洁。
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
4. Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)或 .then(undefined, rejection)的别名。用于指定发生错误时的回调函数。
catch不仅会抛出promise里的错误,还会抛出then方法里执行时候的错误,还能抛出前面catch里执行的错误。 所以一般不建议then里面同时写resolve和reject的回调处理函数。而是写一个catch回调函数替代then方法里reject的回调函数。
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
5. Promise.prototype.finally()
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
它的实现也很简单。
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
) ;
};
上面代码中,不管前面的 Promise 是fulfilled还是rejected,都会执行回调函数callback。
6. Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。













网友评论