Promise.then() 链式调用 和 async/await 语法

核心差异在于 JavaScript 异步编程的两种范式:Promise.then() 链式调用 和 async/await 语法。这是 JavaScript 处理异步操作(如网络请求、文件读写)的核心知识,下面系统讲解:

核心差异对比表

对比维度第一个函数(.then() 链式调用)第二个函数(async/await 语法)
异步语法使用 Promise.then() 链式回调处理异步逻辑使用 async/await 语法,将异步代码 “同步化” 书写
代码结构嵌套层级深(then 内部套 forEach),易形成 “回调地狱”线性代码结构,无嵌套,可读性更高
错误处理仅判断 res.code !== 0,未捕获请求本身的异常(如网络错误)可配合 try/catch 捕获所有异步异常(需手动添加,见下文)
执行流程控制外部无法等待函数内异步逻辑完成(函数返回 Promise 但未暴露结果)函数本身返回 Promise,外部可通过 await 等待所有逻辑完成
变量作用域res 仅在 then 回调内部可见,外部无法访问res 在整个函数内可见,便于后续扩展逻辑(如多步骤数据处理)

逐行拆解关键差异

1. 异步逻辑的 “书写方式” 不同(最直观)

第一个函数用 .then() 包裹异步结果处理,代码是 “嵌套式” 的:

// 第一个函数:.then() 链式调用,逻辑嵌套在回调里
getDeviceTopo().then(async (res) => {
  // 所有数据处理逻辑都在这个回调函数内部
  if (res.code !== 0) return;
  if (Array.isArray(res.data)) {
    res.data.forEach(...) // 又一层嵌套
  }
});

第二个函数用 async/await 把异步逻辑 “拉平”,代码是 “线性式” 的:

// 第二个函数:await 等待结果,逻辑按顺序书写
async function getDevicesList() {
  // 1. 等待请求完成(同步式书写,无嵌套)
  const res = await getDeviceTopo();
  // 2. 按顺序处理结果(和同步代码一样顺下来)
  if (res.code !== 0) return;
  if (Array.isArray(res.data)) {
    res.data.forEach(...) // 仅一层循环,无回调嵌套
  }
}
外部调用时的 “可控性” 不同(最关键)

假设在 onMounted 中调用函数,需要等待数据加载完成后再执行 addStarbillboardAll()

  • 第一个函数无法直接等待:因为 getDeviceTopo().then() 是 “内部异步”,函数本身会立即执行完并返回 undefined,外部无法知道内部数据是否处理完成:

onMounted(() => {
  getDevicesList(); // 函数立即执行完,但内部 .then() 还在等数据
  addStarbillboardAll(); // 此时 snCoordinateList 还是空的,执行无效
});

第二个函数可通过 await 等待:因为函数被声明为 async,会返回一个 Promise,外部用 await 就能等到内部所有异步逻辑完成:

onMounted(async () => {
  await getDevicesList(); // 等待请求+数据填充完成
  addStarbillboardAll(); // 此时 snCoordinateList 已填充,执行有效
});

错误处理的 “完整性” 不同(易忽略)

第一个函数仅处理了 “接口返回 code !== 0” 的业务错误,但没处理 “请求失败” 的网络错误(如断网、超时):

// 第一个函数:若 getDeviceTopo() 本身报错(如网络断了),错误会“丢失”
getDeviceTopo().then(async (res) => {
  if (res.code !== 0) return; // 仅处理业务错误
}).catch(err => {
  // 原代码没写 catch,错误会静默失败,无法排查
});

第二个函数可通过 try/catch 捕获所有错误(网络错误 + 业务错误),可控性更强:

// 第二个函数:完善的错误处理
async function getDevicesList() {
  try {
    const res = await getDeviceTopo(); // 网络错误会进入 catch
    if (res.code !== 0) { // 业务错误在此处理
      console.warn('业务错误:', res.code);
      return;
    }
    // 数据处理...
  } catch (err) {
    // 捕获所有异步错误(网络断了、超时等)
    console.error('请求失败:', err);
  }
}

总结:为什么推荐第二个函数?

  1. 可读性更高:线性代码比嵌套回调更易理解,尤其后续扩展多步骤异步逻辑时(如 “请求 1→处理 1→请求 2→处理 2”);
  2. 可控性更强:外部可通过 await 等待函数内所有异步操作完成,避免 “数据未就绪就执行后续逻辑” 的 bug;
  3. 错误更易处理:配合 try/catch 可统一捕获所有异常,便于排查问题(如网络错误、数据格式错误)。

唯一需要注意的是:async 函数会默认返回 Promise,调用时需用 await 或 .then() 处理,避免直接同步调用导致逻辑错位。

特别说明

为什么getDeviceTopo().then() 是 “内部异步”,函数本身会立即执行完并返回 undefined,外部无法知道内部数据是否处理完成:

要理解这个问题,需要先搞清楚 JavaScript 函数的执行机制 和 Promise 的工作原理。核心原因是:getDeviceTopo().then() 中的异步逻辑(.then() 里的代码)会 “脱离” 函数的主流程,函数本身会在发起异步请求后立即结束,导致外部无法感知内部异步操作的完成状态。

一、拆解函数执行的全过程

function getDevicesList() {
  snCoordinateList.value = [];
  // 步骤1:调用 getDeviceTopo(),发起异步请求(如接口调用)
  getDeviceTopo().then(async (res) => {
    // 步骤3:异步操作完成后才执行(可能是1秒后)
    if (res.code !== 0) return;
    // ... 处理数据并填充 snCoordinateList ...
  });
  // 步骤2:函数在这里直接结束,返回 undefined
}
关键时间线:
  1. 函数开始执行snCoordinateList.value = [] 同步执行(立即完成)。
  2. 发起异步请求getDeviceTopo() 被调用,这是一个异步操作(比如网络请求),会 “立即发起” 但 “不会立即完成”。
  3. 注册回调函数.then(...) 中的回调函数会被 “暂存” 起来,等待异步操作(getDeviceTopo())完成后再执行。
  4. 函数提前结束:注册完回调后,getDevicesList() 函数就执行完毕了,返回默认值 undefined(此时异步操作可能还在进行中,.then() 里的代码完全没执行)。
  5. 异步操作完成:一段时间后(比如接口返回数据),暂存的 .then() 回调才会被执行,开始处理数据。

二、为什么外部无法知道内部是否完成?

因为 函数的返回值和内部异步逻辑的执行结果完全脱节

  • getDevicesList() 函数本身的执行在 “发起异步请求并注册回调” 后就结束了,返回 undefined,外部调用者只能拿到这个 undefined,无法获取异步操作的结果。
  • .then() 里的代码是 “未来才会执行” 的逻辑,它的执行结果(比如填充 snCoordinateList)不会影响 getDevicesList() 已经返回的 undefined

举个生活例子:你让同事(getDevicesList)去取快递(getDeviceTopo),并叮嘱他 “取到后放前台(.then() 处理数据)”。同事听完后立刻告诉你 “我知道了”(函数返回 undefined),然后就去忙别的了。你只知道他 “接了任务”,但不知道他 “什么时候取到快递”“有没有放到前台”—— 这就是外部无法感知内部异步逻辑的状态。

三、async/await 为什么能解决这个问题?

第二个函数用 async/await 改写后,本质是让函数 “等待” 异步操作完成后再结束:

async function getDevicesList() {
  snCoordinateList.value = [];
  // 步骤1:等待 getDeviceTopo() 完成,拿到结果后再继续
  const res = await getDeviceTopo();
  // 步骤2:异步操作完成后,才执行数据处理
  if (res.code !== 0) return;
  // ... 处理数据并填充 snCoordinateList ...
  // 步骤3:所有逻辑完成后,函数才返回(返回一个 Promise)
}
关键变化:
  • async 关键字让函数默认返回一个 Promise 对象(而非 undefined)。
  • await 会让函数 “暂停” 执行,直到 getDeviceTopo() 的异步操作完成,拿到 res 后才继续往下走。
  • 函数的结束时机被 “延后” 到了所有异步逻辑(包括数据处理)完成之后。

此时外部调用者可以通过 await 等待这个 Promise,直到内部所有操作完成:

// 外部知道要等内部完成后再执行下一步
await getDevicesList(); 
addStarbillboardAll(); // 此时 snCoordinateList 已填充

总结核心差异

函数写法函数返回值函数结束时机外部能否感知内部异步完成?
getDeviceTopo().then()undefined发起异步请求并注册回调后立即结束不能(完全脱节)
async/awaitPromise 对象等待所有异步操作 + 数据处理完成后结束能(通过 await 等待)

简单说:then() 写法中,函数 “甩手掌柜” 式地发起异步后就结束了;async/await 写法中,函数会 “负责到底”,等所有异步逻辑完成后才结束,因此外部能明确知道何时可以使用处理后的数据。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值