本文主要是作为Promise面试常用知识梳理。如有错误或建议,欢迎指出,将在第一时间改进。
最近,一些笔记知识改在Github Issues更新了,欢迎 star, comment, folow 。
概念
JS是单线程,浏览器是多线程,需要事件循环去决定异步操作(定时器、请求、事件监听等)事件的顺序。使用回调函数处理异步,你不知道第三方库到底会怎么执行回调(多次执行),以及在回调函数中再嵌套回调函数会导致代码非常难以维护。
针对回调函数的缺点,ES6引入Promise
对象。
- 引入链式调用,每个then方法同样也是一个Promise。
- Promise的then方法会接受2个函数,一个是被resolve时执行的回调,另一个是被reject时执行的回调。
- Promise所有响应的处理回调都是异步调用的,不会阻塞代码的执行,Promise将then方法的回调放入一个叫微任务的队列中(MicroTask),确保这些回调任务在同步任务执行完以后再执行。
用法
一个异步加载图片的例子
1
2
3
4
5
6
7
8
9
10
11
12
|
function loadImageAsync(url){
return new Promise(function(resolve, reject){
const image = new Image();
image.onload = function(){
resolve(image);
};
image.onerror = function(){
reject(new Error('can not load image at' + url));
};
image.src = url;
});
}
|
Image() 方法会创建一个新的HTML Image Element对象
方法
Promise.prototype.then()
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为resolved
时调用,第二个回调函数是Promise
对象的状态变为rejected
时调用。
then的链式写法
1
2
3
4
5
6
|
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => ("resolved: ", comments),
err => ("rejected: ", err)
);
|
then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收。
Promise.prototype.catch()
1
2
3
4
5
6
7
|
promise
.then((data) => {
console.log('resolved',data);
})
.catch((err) => {
console.log('rejected',err);
});
|
1
2
3
4
5
6
|
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
|
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。一般 Promise 对象后面要跟catch
方法,这样可以处理 Promise 内部发生的错误。catch
方法返回的还是一个 Promise 对象,因此后面还可以接着调用then
方法。
Promise.prototype.finally()
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES9 引入标准的。
1
2
3
4
5
|
server.listen(port)
.then(function () {
// ...
})
.finally(server.stop); // 最后关掉服务器
|
Promise.all()
Promise的all方法一般接收一个数组参数,里面的值最终都算返回Promise对象,并且在所有异步操作执行完后才执行回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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: 报错了]
|
如果p2
没有自己的catch
方法,才会调用Promise.all()
的catch
方法。
Promise.race()
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
//请求某个图片资源
function requestImg(){
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
console.log(data);
}).catch((err) => {
console.log(err);
});
|
Promise.resolve()
- 参数是一个 Promise 实例,不做任何修改、原封不动地返回这个实例。
- 参数是一个原始值,或者是一个不具有
then
方法的对象,则返回一个新的 Promise 对象,状态为resolved
。
- 参数是一个具有
then
方法的对象,会将这个对象转为 Promise 对象,然后立即执行then
方法。
1
2
3
4
5
6
7
8
9
10
|
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
|
Promise.reject()
把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
let p = new Promise((resolve, reject) => { // Promise构造函数接收一个函数作为参数
//做一些异步操作
setTimeout(function() {
var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
if (num <= 5) {
resolve(num);
} else {
reject('数字太大了');
}
}, 2000);
});
p.then((data) => {
console.log('resolved', data);
}, (err) => {
console.log('rejected', err);
});
|
推荐文章
Promise 对象-by阮一峰
近一万字的ES6语法知识点补充
Promise的源码实现(完美符合Promise/A+规范)