认识 Promise

认识 Promise

前言:为什么会出现 Promise?

最常见的一个场景就是 ajax 请求,通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。

在 ajax 的原生实现中,利用了 onreadystatechange 事件,当该事件触发并且符合一定条件时,才能拿到想要的数据,之后才能开始处理数据,这样做看上去并没有什么麻烦,但如果这个时候,我们还需要另外一个 ajax 请求,这个新 ajax 请求的其中一个参数,得从上一个 ajax 请求中获取,这个时候我们就不得不等待上一个接口请求完成之后,再请求后一个接口

js 复制代码
let xhr = new XMLHttpRequest();
xhr.open('get', 'https://...');
xhr.send();
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log(xhr.responseText)

      //伪代码....
      let xhr = new XMLHttpRequest();
      xhr.open('get','http://www.xx.com?a'+xhr.responseText);
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
          if(xhr.status>=200 && xhr.status<300){
            console.log(xhr.responseText)
          }
        }
      }
    }
  }
}

当出现第三个 ajax(甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。

这场灾难,往往也被称为回调地狱

因此我们需要一个叫做 Promise 的东西,来解决这个问题,当然,除了回调地狱之外,还有个非常重要的需求就是:为了代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来

上面的写法,是完全没有区分开,当数据变得复杂时,也许我们自己都无法轻松维护自己的代码了。这也是模块化过程中,必须要掌握的一个重要技能,请一定重视。

1. Promise 是什么?

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。

ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题

旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs 模块、ajax、数据库操作

  • 从语法上说,Promise 是一个构造函数;
  • 从功能上说,Promise 对象用来封装一个异步操作并可以获取其成功 / 失败的结果值。

2. Promise 的优点

  1. 指定回调函数的方式更加灵活

    1. 旧的方法:必须在启动异步任务前指定

    2. promise:启动异步任务 -> 返回 promise 对象 -> 给 promise 对象绑定回调函数

      (甚至可以在异步任务结束后指定多个)

  2. 可以解决回调地狱问题,支持链式调用

    1. 什么是回调地狱?

      回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

    2. 回调地狱的缺点?

      不便于阅读、不便于异常处理

    3. 解决方案?

      promise链式调用

    4. 终极解决方案

      async / await

3. 定义 Promise 对象

js 复制代码
new Promise((resolve, reject) => { ... });

3.1 Promise 实例对象的两个属性

  1. PromiseState

    此属性为 promise 对象的状态属性。

    • fulfilled:成功的状态
    • rejected:失败的状态
    • pending:初始化的状态

    【注】状态只能由 pending -> fulfilled 或是 pending -> rejected

  2. PromiseResult

    此属性为 promise 对象的结果值(resolve 以及 reject 函数的形参值)

3.2 Promise 实例对象的两个参数

  1. resolve

    修改 promise 对象的状态,由 pending 修改为 fulfilled;

    将实参设置到这个属性 PromiseResult 中。

  2. reject

    修改 promise 对象的状态,由 pending 修改为 rejected;

    将实参设置到这个属性 PromiseResult 中。

js 复制代码
let p = new Promise((resolve, reject) => {
  // 调整状态
  // reject(new Error("error")); // 状态为 rejected
  resolve("success"); // 状态为 resolved
});
console.log(p); // Promise { <pending> }

4. Promise 对象的状态

Promise 对象通过自身的状态来控制异步操作,Promise 实例具有三种状态.

  1. 异步操作未完成:pending
  2. 异步操作成功:fulfilled
  3. 异步操作失败:rejected

这三种的状态的变化途径只有两种

  • 从 pending(未完成)到 fulfilled(成功)
  • 从 pending(未成功)到 rejected(失败)

一旦状态发生变化,就凝固了,不会再有新的状态变化,这也是 Promise 这个名字的由来,它的英语意思 "承诺",一旦承诺生效,就不得再改变了,这也意味着 Promise 实例的状态变化只可能发生一次

在 Promise 对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理 Promise 的状态变化。

上面的 resolve 和 reject 都为一个函数,他们的作用分别是将状态修改为 resolved 或 rejected。

因此,Promise 的最终结果只有两种情况:

  1. 异步操作成功,Promise 实例传回一个值(value),状态变为 fulfilled;
  2. 异步操作失败,Promise 实例抛出一个错误(error),状态变为 rejected

5. then 方法(重要)

实例化 Promise 时,使用回调函数作为参数,回调函数通常有两个参数:

  1. resolve 参数

    当执行到 resolve( ... ) 时,会调用 then 方法中的第一个参数(回调);

  2. reject 参数

    当执行到 reject( ... ) 时,会调用 then 方法中的第二个参数(回调);

then 方法中通常有两个回调函数作为参数,第一个回调在成功时(resolve)调用,第二个回调在出错时(reject)调用,第二个参数可以省略。

5.1 then 方法返回结果

调用 then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定

  1. 返回结果是非 Promise 类型的属性

    返回状态 resolved(成功),返回值为对象成功的值

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        return 123;
      },
      (error) => { console.warn(error); }
    );
    
    console.log(result); // 返回值为 123

    如果未使用 return 进行返回,则返回值为 undefined。

  2. 返回 Promise 对象

    返回值和返回状态均由返回的 promise 对象的返回值和状态决定

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        return new Promise((resolve, reject) => {
          resolve("ok");
          // reject("出错了");
        });
      },
      (error) => { console.warn(error); }
    );
    console.log(result); // 返回状态为 resolved,返回值为 ok
    // console.log(result); // 返回状态为 rejected,返回值为 出错了
  3. 抛出错误

    返回状态 rejected(失败)

    js 复制代码
    const result = p.then(
      (data) => {
        console.log(data);
        // throw new Error("出错了");
        throw "出错了";
      },
      (error) => { console.warn(error); }
    );
    
    console.log(result); // 返回状态为 rejected,返回值为 出错了

5.2 then 方法的链式调用

由于 promise 可以返回 promise 对象,因此可以进行链式调用

js 复制代码
// 链式调用
p.then(
  (data) => {},
  (error) => {}
).then(
  (data) => {},
  // 失败回调可以省略
)...;

下一节:Promise 详细说明

相关推荐
前端小D5 天前
ES6 中的 Promise
前端·javascript·es6·promise
qwert10376 天前
跨域问题解释及前后端解决方案(SpringBoot)
spring boot·后端·okhttp
符哥20088 天前
基于 OkHttp+Retrofit 实现 JSON / 表单 / XML/Protobuf 数据格式全解析
okhttp·json·retrofit
XiaoLeisj10 天前
Android 网络编程入门到实战:HttpURLConnection、JSON 处理、OkHttp 与 Retrofit2
android·网络·okhttp·json·gson·retrofit2·jsonobjecy
凯鑫BOSS10 天前
Pbootcms查看详细报错信息
okhttp
小怪点点16 天前
手写promise
前端·promise
willow17 天前
Promise由浅入深
javascript·promise
vistaup1 个月前
OKHTTP 默认构建包含 android 4.4 的TLS 1.2 以及设备时间不对兼容
android·okhttp
Irene19911 个月前
Promise 未捕获 reject 错误处理指南
promise·错误处理
bug-0072 个月前
关于前后端自动携带cookie跨域问题
okhttp