Promise到底是个啥玩意

1. 回调地域

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

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

  1. 获取产品类别
js 复制代码
// 1. 获取类别信息
const getCategory = () => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: [
      { id: 1, name: "水果" },
      { id: 2, name: "图书" },
    ],
  };
  return res;
};
  1. 根据类别 id 获取产品信息
js 复制代码
// 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;
};
  1. 根据产品 id 获取产品价格
js 复制代码
// 3. 根据产品id获取产品价格
const getPriceByProductId = (productId) => {
  // 模拟请求
  let res = {
    code: 200,
    message: "请求成功",
    data: { id: 1001, productId: 111, price: 112.23, unitPrice: 1235.23 },
  };
  return res;
};

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

js 复制代码
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

  1. 获取类别信息
js 复制代码
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);
      }
    });
  };
  1. 根据类别 id 获取产品信息
js 复制代码
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);
      }
    });
  };
  1. 根据产品 id 获取产品价格
js 复制代码
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);
      }
    });
  };

4.获取第一个类别下第一个产品的单位价格信息

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

js 复制代码
  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;
    })
  };

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

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

4. async 和 await

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

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

语法:

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

举例:

js 复制代码
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 对象

js 复制代码
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.同步请求就会依次打印:1、2。如果第一个方法执行时间比较长,那就一直等待。

js 复制代码
const getProductPrice =  () => {
    console.log("1")
};
console.log("2")

2.异步请求可能会先打印 2,后打印 1。

js 复制代码
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 什么关系

1.async/await 对 promise 进行了封装。

2.async/await 是用同步语法去获取异步请求,彻底消灭回调函数。

3.只有 async,返回的是 Promise 对象。

4.await 相当于 Promise 的 then

相关推荐
卡兰芙的微笑21 分钟前
get_property --Cmakelist之中
前端·数据库·编辑器
覆水难收呀24 分钟前
三、(JS)JS中常见的表单事件
开发语言·前端·javascript
猿来如此呀31 分钟前
运行npm install 时,卡在sill idealTree buildDeps没有反应
前端·npm·node.js
hw_happy37 分钟前
解决 npm ERR! node-sass 和 gyp ERR! node-gyp 报错问题
前端·npm·sass
FHKHH41 分钟前
计算机网络第二章:作业 1: Web 服务器
服务器·前端·计算机网络
视觉小鸟1 小时前
【JVM安装MinIO】
前端·jvm·chrome
二川bro2 小时前
【已解决】Uncaught RangeError: Maximum depth reached
前端
qq22951165023 小时前
python毕业设计基于django+vue医院社区医疗挂号预约综合管理系统7918h-pycharm-flask
前端·vue.js·express
WebGIS皮卡茂3 小时前
【数据可视化】Arcgis api4.x 热力图、时间动态热力图、timeSlider时间滑块控件应用 (超详细、附免费教学数据、收藏!)
javascript·vue.js·arcgis·信息可视化
八了个戒3 小时前
Koa (下一代web框架) 【Node.js进阶】
前端·node.js