es6 Promise

一、 promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

1. promise 的特点

  1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
  1. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 resolved 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

2. promise 优缺点

  • 优点: 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

  • 缺点: 首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

二、 Promise 创建

要想创建一个 promise 对象、可以使用 new 来调用 Promise 的构造器来进行实例化。

下面是创建 promise 的步骤:

var promise = new Promise(function (resolve, reject) {
  // 异步处理
  // 处理结束后、调用resolve 或 reject
});

Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。

实例

var myFirstPromise = new Promise(function (resolve, reject) {
  //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
  //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
  setTimeout(function () {
    resolve("成功!"); //代码正常执行!
  }, 250);
});

myFirstPromise.then(function (successMessage) {
  //successMessage的值是上面调用resolve(...)方法传入的值.
  //successMessage参数不一定非要是字符串类型,这里只是举个例子
  document.write("Yay! " + successMessage);
});

三、 Promise Ajax

//搜索 `海阔天空` ,得到的结果是数组
function p1() {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: `http://netease2.bluej.cn/search`,
      data: {
        keywords: "海阔天空",
      },
      success(res) {
        console.log("函数里 p1 自己的res", res);
        resolve(res); //把 p1 的 结果 给then
      },
      error(err) {
        reject(err); //把 p1 的 报错 给catch
      },
    });
  });
}

四、 Promise then

// p1_res:自定义的变量,存储上面p1的res
function p2(p1_res) {
  return new Promise((resolve, reject) => {
    //得到数组中的第一首歌
    let song = p1_res.result.songs[0];
    //得到第一首歌的歌手id
    let id = song.artists[0].id;
    $.ajax({
      url: `http://netease2.bluej.cn/artist/desc`,
      data: {
        id: id,
      },
      success(res) {
        //获取歌手的详情
        console.log("歌手详情", res);
        resolve(res); //把 p2 的 结果 留给后面可能用到的then
      },
      error(err) {
        reject(err); //把 p2 的 报错 给catch
      },
    });
  });
}

现在有 p1 和 p2 两个方法,p2 的执行需要等待 p1 的结果

//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
  .then(p2)
  .then((p2_res) => {
    console.log("p2给的", p2_res);
  });

五、 Promise catch

//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
  .then(p2)
  .then((p2_res) => {
    console.log("p2给的", p2_res);
  })
  .catch((err) => {
    console.log("catch", err);
  }); //输出错误信息,如果是p1报错,那么p2就不执行

六、 Promise finally

//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
  .then(p2)
  .then((p2_res) => {
    console.log("p2给的", p2_res);
  })
  .catch((err) => {
    console.log("catch", err);
  }) //输出错误信息,如果是p1报错,那么p2就不执行
  .finally(() => {}); //最后不管结果怎样都会执行一次

七、 Promise race

function step1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // console.log('step 1 finish');
      resolve("111");
    }, 2000);
  });
}
function step2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // console.log('step 2 finish');
      resolve("2222");
    }, 4000);
  });
}
// 同时执行多个异步操作,但是只取最快resolve的promise结果
Promise.race([step1(), step2()]).then((result) => {
  console.log("race finish", result);
});

八、 Promise all

function step1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // console.log('step 1 finish');
      resolve("111");
    }, 2000);
  });
}
function step2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // console.log('step 2 finish');
      resolve("2222");
    }, 4000);
  });
}

//所有异步操作都成功后才执行(等最慢的那个),返回所有实例的结果
Promise.all([step1(), step2()]).then((result) => {
  console.log("all finish", result);
});

1. promise all 的缺陷

例子:

Promise.all([A(), B(), C(), D()])
  .then((res) => {
    this.resA = res[0];
    this.resB = res[1]; // 这个数据拿不到
    this.resC = res[2];
    this.resD = res[3];
  })
  .catch((err) => {
    console.log(err);
  });

promise.all 的用法: 传递一个 promise 的数组,当所有的 promise 都完成(resolved),回调所有成功的结果 但是有一个失败, 回调第一个失败的结果,不执行then回调 在当前状况下, hobbies 接口返回 500,promise 变成 rejected 状态,然后就不会执行 then 回调了。

2. 什么时候可以使用 Promise.all

既然一个 Promise 失败会影响所有 Promise 回调执行以及 promise.all 会有一些阻塞的情况,那么我们什么情况可以使用 Promise.all 呢? 例如: 几个异步操作是强相关的,后续操作必须依赖这几个返回结果才能进行。 举个栗子: 点餐查询用户的账号状态,用户的余额,会员信息,产品库存,商家状态等才能下单

Promise.all([A(), B(), C(), D()])
  .then(() => {
    // 下单和支付的操作
  })
  .catch((error) => console.log("异常订单", error));

3. 如何解决

.then。因为.then返回的也是一个 promise 对象,不管是resolve还是reject都会执行.then。返回promise对象后就变成resovle状态了。

function handlePromise(promiseList) {
  return promiseList.map((promise) =>
    promise.then(
      (res) => ({ status: "success", res }),
      (err) => ({ status: "fail", err })
    )
  );
}

Promise.all(
  handlePromise([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])
).then(
  (res) => console.log(res),
  (err) => console.log(err)
);

// [
//  {status: "fail", err: 1},
//  {status: "success", res: 2},
//  {status: "success", res: 3}
// ]
Last Updated:
Contributors: zerojs