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

相关推荐
却尘9 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare10 分钟前
浅浅看一下设计模式
前端
Lee川13 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix40 分钟前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人43 分钟前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空1 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust