重学ES6之Promise

本文主要是作为Promise面试常用知识梳理。如有错误或建议,欢迎指出,将在第一时间改进。

最近,一些笔记知识改在Github Issues更新了,欢迎 star, comment, folow 。

ES6

概念

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+规范)

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy