关于Promise

一个人想着一个人 提交于 2019-11-29 08:14:56

Javascript 中的Promise

一,什么是 Promise

Promise 是异步编程的一种解决方案---回调函数中的回调地狱(callback hell)。ES6 将其写进了语言标准,统一了用法。简单的说 Promise就是一个容器,里面保存着某个未来才会结束的事件

一个 Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

codeacademy中的一个洗碗机的例子就很形象:

  • Pending: 洗碗机在工作,但是没有完成清洗流程
  • Fulfilled: 洗碗机完成了所有清洗流程,并且装满了干净的餐具
  • Rejected: 洗碗机工作过程中出了问题(比如没加洗洁精),结果里面都是不干净的餐具

使用Promise前先看个简单例子热身:

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

myFirstPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(...)方法传入的值.
    //successMessage参数不一定非要是字符串类型
    console.log("Yay! " + successMessage);
});

二,Promise的使用

1,使用约定
  • 在 本轮事件循环(event loop)运行完成之前,回调函数是不会被调用的
  • 通过 then() 添加的回调函数总会被调用,即便它是在异步操作完成之后才被添加的函数
  • 通过多次调用 then(),可以添加多个回调函数,它们会按照插入顺序一个接一个独立执行

因此,Promise 最直接的好处就是链式调用chaining)。

2,链式调用

在ES6之前,多重的异步操作,会导致经典的回调地狱:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

ES6之后,可以把回调绑定到被返回的 Promise 上代替以往的做法,形成一个 Promise 链:

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

注意:一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。

在 ECMAScript 2017 标准的 async/await 语法糖中,使得逻辑更加的清晰

async function foo() {
  try {
    let result = await doSomething();
    let newResult = await doSomethingElse(result);
    let finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

这跟我之前使用Python中的异常捕获有着异曲同工之妙。

3,组合使用

Promise.resolve()]和 Promise.reject() 是手动创建一个已经 resolve 或者 reject 的Promise的快捷方法[Promise.all() 和 [Promise.race() 是并行运行异步操作的两个组合式工具。

比如:

Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

发起并行操作,然后等多个操作全部结束后进行下一步操作。

4,关于时序

即使是一个已经变成 resolve 状态的 Promise,传递给 then() 的函数也总是会被异步调用。传递到 then() 中的函数被置入了一个微任务队列,而不是立即执行,这意味着它是在 JavaScript 事件队列的所有运行时结束了,事件队列被清空之后,才开始执行:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

wait().then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4

三,Promise的特点小结

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!