Promise到底是个啥?你知道吗?

开发 前端
Promise 对 axios 的返回结果进行了封装。所以当你发送一个 axios 请求,会返回一个 Promise 对象。然后你就可以调用 .then、.catch方法了。

1. 回调地域

在学习 Promise 之前,我们先看一下什么是回调地狱:

这里我们模拟三个请求接口:

  • 获取产品类别
// 1. 获取类别信息
const getCategory = () => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: [
      { id: 1, name: "水果" },
      { id: 2, name: "图书" },
    ],
  };
  return res;
};
  • 根据类别 id 获取产品信息
// 2. 根据类别 id 获取产品信息
const getProductByCategoryId = (categoryId) => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: [
      { id: 111, categoryId: 1, name: "苹果" },
      { id: 112, categoryId: 1, name: "香蕉" },
      { id: 113, categoryId: 1, name: "百香果" },
    ],
  };
  return res;
};
  • 根据产品 id 获取产品价格
// 3. 根据产品id获取产品价格
const getPriceByProductId = (productId) => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: { id: 1001, productId: 111, price: 112.23, unitPrice: 1235.23 },
  };
  return res;
};

接下来我们去获取第一个类别下第一个产品的单位价格信息:

let unitPrice = 0.0;
// 获取产品价格
const getProductPrice = () => {
  let categoryRes = getCategory();
  if (categoryRes.code === 200) {
    let produceRes = getProductByCategoryId(1);
    if (produceRes.code === 200) {
      let priceRes = getPriceByProductId(1);
      if (priceRes.code === 200) {
        unitPrice = res.data.unitPrice;
      }
    }
  }
};

我们发现在 getProductPrice 这个方法中,第一个请求接口的结果要作为第二个请求接口的参数,以此类推就会出现层层嵌套,回调里面套回调,这就是所谓的“回调地狱”。

如果请求的接口太多,那代码写起来可就太苦逼了,就跟套娃一样。

所以为了解决回调地狱的问题,提高代码的可读性,Promise 应运而生。

2. 邂逅 Promise

Promise 是异步编程的一种解决方案。它本质上是一个构造函数,可以构建一个 Promise 对象。

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject 。对象上有 then、catch、finall 方法。

Promise 有三种状态:pending、fulfilled、rejected:

  • pending:它的意思是待定的,相当于是一个初始状态。创建 Promise 对象时就是一个初始状态。
  • fulfilled:成功。当调用 resolved 方法后,Promise 对象的状态就会从 pending 切换到 fulfilled,且不可改变。
  • rejected:失败。当调用 reject 方法后,Promise 对象的状态就会从 pending 切换到 reject,且不可改变。

看了上面的解释,你可能还是有点懵逼,接下来我用通俗易懂的语言解释一下 Promise 是个什么玩意:

1.Promise 就是一个用来封装 HTTP 请求的工具。

2.我们请求后台接口获取数据,如果请求成功,就调用 Promise 的 resolve 方法,并将返回的数据作为该方法的参数。如果请求失败,就调用 Promise 的 reject 方法,并将返回的错误信息作为该方法的参数。

3.然后我们将一个 Promise 对象作为该请求接口的返回值返回。

4.因为该接口返回一个 Promise 对象,所以我们调用该接口的时候就可以直接用.then()处理成功的信息,用 .catch() 处理失败的信息了。

接下来我们将上面的例子用 Promise 改造一下,有了真实案例,大家对 Promise 的理解就更清晰明了了。

3. 使用 Promise

  • 获取类别信息
const getCategory = () => {
    // 返回结果封装成 Promise 对象
    return new Promise((resolve, reject) => {
      // 模拟请求
      let res = {
        code: 200,
        message: "请求成功",
        data: [
          { id: 1, name: "水果" }
        ],
      };
      if (res.code == 200) {
        resolve(res);
      } else {
        reject(res);
      }
    });
  };
  • 根据类别 id 获取产品信息
const getProductByCategoryId = (categoryId) => {
    return new Promise((resolve, reject) => {
      // 模拟请求
      let res = {
        code: 200,
        message: "请求成功",
        data: [
          { id: 111, categoryId: 1, name: "苹果" },
          { id: 112, categoryId: 1, name: "香蕉" },
          { id: 113, categoryId: 1, name: "百香果" },
        ],
      };
      if (res.code == 200) {
        resolve(res);
      } else {
        reject(res);
      }
    });
  };
  • 根据产品 id 获取产品价格
const getPriceByProductId = (productId) => {
    return new Promise((resolve, reject) => {
      // 模拟请求
      let res = {
        code: 200,
        message: "请求成功",
        data: { id: 1001, productId: 111, price: 112.23, unitPrice: 1235.23 },
      };
      if (res.code == 200) {
        resolve(res);
      } else {
        reject(res);
      }
    });
  };
  • 获取第一个类别下第一个产品的单位价格信息

Promise 最常用的就是链式调用格式:

let unitPrice = 0.0;
  // 获取产品价格
  const getProductPrice = () => {
    getCategory().then(res => {
      // 类别 id
      let id = res.data[0].id;
      // 返回一个 Promise 对象
      return getProductByCategoryId(id);
    }).then(res => {
      // 产品 id
      let id = res.data[0].id;
      return getPriceByProductId(id);
    }).then(res => {
      unitPrice = res.data.unitPrice;
    })
  };

当然我们在日常使用过程中一般都是这种格式:

getMethod().then(res => {
  // 请求成功
  }).catch(error=>{
  // 异常
  }).finally(()=>{
  // 不管成功还是异常都要执行
  })

4. async 和 await

虽然有了 Promise 之后,代码的可读性有了很大提高。但是 ES7 又引入了 async 和 await 来简化 Promise 调用操作,实现了以异步操作像同步的方式去执行。

说白了async 和 await 就是对 Promise 进行了封装。

语法:

await 和 async 是成对出现的,如果写了 await 必须要写 async,否则会报错。如果只写 async,那返回的就是一个 Promise 对象

举例:

let unitPrice = 0.0;
// 获取产品价格
const getProductPrice = async () => {
  let res1 = await getCategory();
  let categoryId = res1.data[0].id;
  let re2 = await getProductByCategoryId(categoryId);
  let productId = re2.data[0].id;
  let re3 = await getPriceByProductId(productId);
  unitPrice = res3.data.unitPrice;
};

如果只写 async,返回的就是一个 Promise 对象

const getProductPrice = async () => {
  getCategory().then(res=>{
    let categoryId =  res.data[0].id
  })
};
const getCategory = async () => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: [
      { id: 1, name: "水果" },
      { id: 2, name: "图书" },
    ],
  };
  return res;
};

那为什么说 async 和 await 实现了异步编程同步化呢?

因为 await 这个命令的意思就是等这一行的异步方法执行成功后,然后才能执行下一行代码,否则就一直等待,下面的代码就执行不了。

所以虽然请求后台的接口是异步的,但是 await 在语法层面实现了同步。

5. 答疑

5.1 同步请求和异步请求

同步请求:当发送一个同步请求时,会暂停后面的代码执行,等待请求的返回结果,然后再继续执行下一行代码。

异步请求:当发送一个异步请求时,会继续执行后面的代码而不会等待请求的返回结果。当请求完成后,再通过回调函数或事件处理函数来处理返回的数据。

  • 同步请求就会依次打印:1、2。如果第一个方法执行时间比较长,那就一直等待。
const getProductPrice =  () => {
    console.log("1")
};
console.log("2")
  • 异步请求可能会先打印 2,后打印 1。
const getProductPrice = async  () => {
    console.log("1")
};
console.log("2")

5.2 promise 和 axios 什么关系

Promise 是 JavaScript 中用于异步编程的一个对象,而 axios 是 用来发送 HTTP 请求的工具库。

Promise 对 axios 的返回结果进行了封装。所以当你发送一个 axios 请求,会返回一个 Promise 对象。然后你就可以调用 .then、.catch方法了。

5.3 promise 和 async/await 什么关系

  • async/await 对 promise 进行了封装。
  • async/await 是用同步语法去获取异步请求,彻底消灭回调函数。
  • 只有 async,返回的是 Promise 对象。
  • await 相当于 Promise 的 then
责任编辑:武晓燕 来源: 知否技术
相关推荐

2015-10-23 09:34:16

2022-05-04 08:38:32

Netty网络框架

2022-12-21 08:04:19

socket图解网络

2024-02-07 12:35:00

React并发模式concurrent

2021-05-11 07:30:58

JNIJavaAPI

2021-01-28 17:41:32

Github网站Pull Reques

2023-12-20 08:23:53

NIO组件非阻塞

2022-04-10 19:26:07

TypeScript类型语法

2016-03-03 17:42:10

DockerDCOS

2024-07-12 15:08:23

Python@wraps函数

2024-07-30 08:22:47

API前端网关

2024-11-08 09:48:38

异步编程I/O密集

2021-12-26 00:01:51

Log4Shell漏洞服务器

2021-12-16 15:11:59

Facebook天秤币加密货币

2024-08-26 14:23:56

2022-09-06 21:38:45

数字人数字孪生

2022-11-28 00:04:17

2024-01-15 12:16:37

2015-12-01 13:33:51

UnikernelLinux运维

2012-07-25 09:09:46

GNOME OS桌面
点赞
收藏

51CTO技术栈公众号