本文是整理阮一峰大神ES6中 Promise 的学习笔记
目录:
- Promise.prototype.then()
- Promise.prototype.catch()
模拟的是catch代码块 - Promise.prototype.finally()
- Promise.all()
全部fulfilled或第一个rejected时回调 - Promise.race()
率先改变的 Promise 实例的返回值,就传递的回调函数 - Promise.allSettled()
全部结束时回调 - Promise.any()
有一个是fulfilled状态,就是fulfilled状态,所有都是rejected状态,才会是rejected状态 - Promise.resolve()
将现有对象转为 Promise 对象 - Promise.reject()
- Promise.try()
模拟try代码块
Promise对象是一个构造函数,用来生成Promise实例
1.promise状态状态不受外界影响
- pending(进行中)
- fulfilled(已成功)resolved(已定型)
- rejected(已失败)
2.状态改变,就不会再变
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:
- 从pending变为fulfilled
- 从pending变为rejected
3.Promise缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
4.代码实例
// 创建promise实例
const promise = new Promise(function(resolve, reject) {
// 这里的代码创建后立即执行
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
// promise.then() 接收两个参数回调函数,成功函数,失败函数
promise.then(function(value) {
// 全部执行完执行这里
// success
}, function(error) {
// failure
});
5.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);
});
6.Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的,返回的是Promise的原型对象的新实例
Promise.prototype ——> new Promise() 原实例
Promise.prototype ——> new Promise().then() 新实例
getJSON("/post/1.json")
.then(function(post) {
// 这里return的值将传入下边的then中
return getJSON(post.commentURL);
})
.then(function (comments) {
// comments这里的参数就是上边return的值
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
7.Promise.prototype.catch()
Promise.prototype.catch与下边等同,但建议用这种方法获取报错,因为Promise 对象的错误具有“冒泡”性质,最后的catch能获取到之前n个then中的所有报错信息
- .then(null, rejection)
- .then(undefined, rejection)
异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,可以写多个catch和多个then,顺序随意
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));
Promise 会吃掉错误
Promise 内部的错误不会影响到 Promise 外部的代码
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
8.Promise.prototype.finally()
- finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
- finally方法的回调函数不接受任何参数
- 没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。
- finally方法里面的操作,与状态无关的,不依赖于 Promise 的执行结果
promise
.finally(() => {
// 语句
});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
9.Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
- Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 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的回调函数。
// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json");
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
promises是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。
const databasePromise = connectDatabase();
const booksPromise = databasePromise
.then(findAllBooks);
const userPromise = databasePromise
.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));
上面代码中,booksPromise和userPromise是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommendations这个回调函数。
注意:如果作为参数的 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方法指定的回调函数。
10.Promise.race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
11.Promise.allSettled()
确保所有操作都结束
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
上面代码对服务器发出三个请求,等到三个请求都结束,不管请求成功还是失败,加载的滚动图标就会消失。
12.Promise.any()
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
console.log(first);
} catch (error) {
console.log(error);
}
上面代码中,Promise.any()方法的参数数组包含三个 Promise 操作。其中只要有一个变成fulfilled,Promise.any()返回的 Promise 对象就变成fulfilled。如果所有三个操作都变成rejected,那么就会await命令就会抛出错误
13.Promise.resolve()
将现有对象转为 Promise 对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
参数:
- 参数是一个 Promise 实例
那么Promise.resolve将不做任何修改、原封不动地返回这个实例。 - 参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。 - 参数不是具有then方法的对象,或根本就不是对象
- 不带有任何参数
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
注意: 立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
setTimeout(fn, 0)在下一轮“事件循环”开始时执行
Promise.resolve()在本轮“事件循环”结束时执行
console.log('one')则是立即执行,因此最先输出
14.Promise.reject()
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable)
})
// true
15.Promise.try()
事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next