Promise 的相关知识,内容包括简介、用法。

一、简介

「异步 + 回调」能够写出异步任务,但是这样的组合,当出现很多回调的时候,就出现了“回调地狱”

前端结合 Promise 和 JS,制定了 Promise 规范,详细描述了 Promise 的原理和使用方法

二、then 和 catch 方法

使用时,在代码中 return 一个 Promise 的实例

  1. return new Promise((resolve,reject)={
  2. // 任务成功,调用 resolve(result)
  3. // 任务失败,调用 reject(error)
  4. })
  • .then(success,fail) 传入成功函数和失败函数
  • 传入的回调函数是异步执行的

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

  1. let promise = new Promise(function(resolve, reject) {
  2. console.log('Promise');
  3. resolve();
  4. });
  5. promise.then(function() {
  6. console.log('resolved.');
  7. });
  8. console.log('Hi!');
  9. // Promise
  10. // Hi!
  11. // resolved

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

  1. // bad
  2. promise
  3. .then(function(data) {
  4. // success
  5. }, function(err) {
  6. // error
  7. });
  8. // good
  9. promise
  10. .then(function(data) { //cb
  11. // success
  12. })
  13. .catch(function(err) {
  14. // error
  15. });

三、Promise.all() 方法

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

  1. 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的回调函数。
  1. // 生成一个Promise对象的数组
  2. const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  3. return getJSON('/post/' + id + ".json");
  4. });
  5. Promise.all(promises).then(function (posts) {
  6. // ...
  7. }).catch(function(reason){
  8. // ...
  9. });

四、Promise.race() 方法

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

  1. const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。

下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve。

  1. const p = Promise.race([
  2. fetch('/resource-that-may-take-a-while'),
  3. new Promise(function (resolve, reject) {
  4. setTimeout(() => reject(new Error('request timeout')), 5000)
  5. })
  6. ]);
  7. p
  8. .then(console.log)
  9. .catch(console.error);

上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

五、async 函数 和 await 命令

  • ES2017 标准引入了 async 函数,它定义了一个异步函数,返回一个 promise
  • 配合 await 会让它的语法和结构会更像是标准的同步函数
  1. // async function 配合 await
  2. async function fn(){
  3. try{
  4. let n = await new Promise(...)
  5. f1(n)
  6. }catch(error){
  7. f2(error)
  8. }
  9. }
  10. // 等同于
  11. new Promise(...).then(f1(n),f2(error))

async函数可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖

  • async函数返回一个 Promise 对象,可以使用then方法添加回调函数
  • 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
  • await 可以等待任意表达式的结果

六、参看文章

「@浪里淘沙的小法师」