在 JavaScript 中,处理异步操作(例如网络请求、文件读写等)是常见的需求。.then() 写法和 async/await 写法是处理这些异步操作的两种主要方式,它们都基于 Promise 对象。其中,async/await 是在 ES2017 (ES8) 中引入的语法糖,旨在让异步代码的编写和阅读更像是同步代码。

1. .then() 写法

.then() 写法使用回调函数和链式调用,解决了回调地狱问题。

链式调用:每次调用 .then() 都会返回一个新的 Promise 对象,这使得我们可以将多个异步操作串联起来,形成一个链条。

调用 .then() 后函数会继续执行,不会阻塞后续代码。回调函数会在 Promse 完成后被放入任务队列执行。这句话什么意思呢?看下面解析:

现在有一个典型的场景:如果说现在有个 Echarts 图表需要后端返回数据

const getList = () => {
  let data: any[] = []

  axios.get("/getList").then((res) => {
    console.log(res.data);
    data = res.data || [] 
  });
  // 然后是下面的其余代码,绘制图表什么的....
  const option = {
    xAxis: {
      type: "category",
      data: data,
      axisLabel: { color: "#4a8ab9", fontSize: 9 },
      axisLine: { lineStyle: { color: "#21b8da", width: 0.5 } }
    },
  }
}

data = res.data || [] 这里你以为是赋值上了吧!!!不!!!并没有!!!

走到这里的时候,其实它只是把这个代码交给其他线程处理了,然后直接往下执行,所以说,还没等赋值 data = res.data || [] 呢,你的代码就已经 data: data 赋值了,所以说页面应该会看不到数据的。

详细分解一下代码的执行顺序:

  • let data: any[] = []: 初始化一个空数组 data。
  • axios.get("/getList"): 发起一个网络请求。这是一个异步操作。JavaScript 引擎不会在这里傻等,它会把这个任务交给浏览器(或 Node.js 环境)去处理,然后立即继续执行下一行代码。
  • .then((res) => { ... });: 为这个网络请求的 Promise 注册一个“成功后的回调函数”。这个回调函数现在不会执行,它被放入了一个任务队列,等待网络请求成功返回后才会被执行。
  • const option = { ... }: 代码立即执行到这里。此时,第 2 步的网络请求很可能还在进行中,第 3 步注册的回调函数也自然还没被调用。所以,这里的 data 变量仍然是第 1 步中定义的那个空数组 []。
  • getList 函数执行完毕。
  • (过了一段时间后...): 网络请求成功,服务器返回了数据。
  • 执行回调函数: JavaScript 引擎从任务队列中取出之前注册的回调函数 (res) => { data = res.data || [] } 并执行它。此时,data 变量才被赋予了从后端获取的值。但为时已晚,包含空数据的 option 对象早已经创建好了。

下面代码才是正确写法:

// 步骤1:改造数据获取函数,让它返回 Promise
const getList = () => {
  // return 出去整个 axios 调用,它本身就是一个 Promise
  return axios.get("/getList");
};


// 步骤2:在调用处使用 .then() 来衔接后续操作
const initChart = () => {
  console.log("开始初始化图表...");

  getList()
    .then(res => {
      // 第一个 .then() 负责从响应中提取和处理数据
      console.log("数据请求成功,正在处理数据...");
      const data = res.data || [];
      return data; // 将处理好的数据 return 出去,传递给下一个 .then()
    })
    .then(data => {
      // 第二个 .then() 接收上一步返回的 data,并执行绘图逻辑
      console.log("数据处理完毕,准备绘制图表:", data);
      const option = {
        xAxis: {
          type: "category",
          data: data, // <-- 这里接收到的 data 就是有值的
          axisLabel: { color: "#4a8ab9", fontSize: 9 },
          axisLine: { lineStyle: { color: "#21b8da", width: 0.5 } }
        },
        // ...
      };
      // 调用绘图
      drawChart(option);
    })
    .catch(error => {
      // .catch() 用于捕获链条中任何环节出现的错误
      console.error("在初始化图表过程中发生错误:", error);
    });
};

// 调用初始化函数
initChart();

所以说 .then()应该 是这样的:

// 使用 .then() 处理异步操作
axios.get("/getList")
  .then(result => {
    console.log(result); // 输出: 数据获取成功!
    // 返回一个新的值,供下一个 .then() 使用
    return result + " (第一次处理)";
  })
  .then(result2 => {
    console.log(result2); // 输出: 数据获取成功! (第一次处理)
    // 也可以返回一个新的 Promise
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(result2 + " (第二次处理)");
      }, 500);
    });
  })
  .then(result3 => {
    console.log(result3); // 输出: 数据获取成功! (第一次处理) (第二次处理)
  })
  .catch(error => {
    // 如果链条中任何一个 Promise 失败,就会被 .catch() 捕获
    console.error(error);
  });

2. async/await 写法

async/await 是构建在 Promise 之上的一种更现代、更简洁的语法。

async 函数:在函数声明前加上 async 关键字,表明这是一个异步函数。异步函数会隐式地返回一个 Promise。

await 操作符:await 只能在 async 函数内部使用。它会暂停 async 函数的执行,等待其后的 Promise 对象的状态变为 settled(无论是成功还是失败),然后恢复执行,并返回 Promise 的结果。

// 使用 async/await 处理异步操作
async function main() {
  try {
    const result = await axios.get("/getList");
    console.log(result); // 输出: 数据获取成功!

    const result2 = result + " (第一次处理)";
    console.log(result2); // 输出: 数据获取成功! (第一次处理)

    const result3 = await processData2(result2);
    console.log(result3); // 输出: 数据获取成功! (第一次处理) (第二次处理)
  } catch (error) {
    // 如果 await 等待的 Promise 失败,错误会被 try...catch 捕获
    console.error(error);
  }
}

最后,可以看一下上一个问题用 async/await 会变得多么简洁直观,因为它就是为了解决这种“同步化”的思维模式而设计的。

const initChartWithAsync = async () => {
  try {
    console.log("开始初始化图表 (async/await)...");

    // 1. await 会“暂停”函数执行,直到 axios.get() 完成,并返回响应
    const res = await axios.get("/getList");

    // 2. 代码能走到这一行,说明上面的 await 已经成功了
    const data = res.data || [];
    console.log("数据获取成功:", data);

    // 3. 像写同步代码一样,直接使用 data
    const option = {
      xAxis: {
        type: "category",
        data: data,
        axisLabel: { color: "#4a8ab9", fontSize: 9 },
        axisLine: { lineStyle: { color: "#21b8da", width: 0.5 } }
      },
      // ...
    };

    drawChart(option);
  } catch (error) {
    // 如果 await 的 Promise 失败了,错误会被 catch 捕获
    console.error("在初始化图表过程中发生错误:", error);
  }
};

// 调用
initChartWithAsync();
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐