Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大
ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例
1、Promise 的特点
- 对象的状态不受外界影响,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected
2、Promise 的基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
resolve 函数的作用是,将 Promise 对象的状态从 “未完成” 变为 “成功”(即从 pending 变为 resolved)
reject 函数的作用是,将 Promise 对象的状态从 “未完成” 变为 “失败”(即从 pending 变为 rejected)
Promise实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then 方法可以接受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,
第二个回调函数是 Promise 对象的状态变为 rejected 时调用,这两个函数都是可选的,不一定要提供
Promise 新建后就会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
如果调用 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 的回调函数将会立刻执行。
注意,调用 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);
})
3、Promise.prototype.then()
Promise 实例具有 then 方法,也就是说,then 方法是定义在原型对象 Promise.prototype 上的
then 方法返回的是一个新的 Promise实例(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
上面的代码使用 then 方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数
采用链式的 then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个 Promise 对象(即有异步操作),这时后一个回调函数,就会等待该 Promise 对象的状态发生变化,才会被调用
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
上面代码中,第一个 then 方法指定的回调函数,返回的是另一个 Promise 对象。这时,
第二个 then 方法指定的回调函数,就会等待这个新的 Promise 对象状态发生变化。
如果变为 resolved,就调用第一个回调函数,如果状态变为 rejected,就调用第二个回调函数
4、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() 方法捕获
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
一般来说,不要在 then() 方法里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用 catch 方法
// bad
promise.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise.then(function(data) {
// success
}).catch(function(err) {
// error
});
5、Promise.prototype.finally()
finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代码中,不管 promise 最后的状态,在执行完 then 或 catch 指定的回调函数以后,都会执行 finally 方法指定的回调函数
6、Promise.all()
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.all([p1, p2, p3]);
只有 p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数
只要 p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数
7、Promise.race()
Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.race([p1, p2, p3]);
只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数
8、Promise.allSettled()
Promise.allSettled() 方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象
Promise.allSettled([
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
]);
只有等到参数数组的所有 Promise 对象都发生状态变更(不管是 fulfilled 还是 rejected),返回的 Promise 对象才会发生状态变更
9、Promise.any()
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => { // 只要有一个 fetch() 请求成功
console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
console.log(error);
});
只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。
Promise.any() 跟 Promise.race() 方法很像,只有一点不同,就是 Promise.any() 不会因为某个 Promise 变成rejected 状态而结束,必须等到所有参数 Promise 变成 rejected 状态才会结束
10、Promise.resolve()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
11、Promise.reject()
Promise.reject('出错了');
// 等同于
new Promise((resolve, reject) => reject('出错了'))
网友评论