CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
JavaScript 核心/课时/异步 Promise 与 async/await
RawBlame
课时目录12 节

JavaScript 核心

  1. 01JavaScript 是什么
  2. 02类型与变量
  3. 03运算符与类型转换
  4. 04条件与循环
  5. 05函数与作用域
  6. 06数组与常用方法
  7. 07对象与 JSON
  8. 08DOM 与事件
  9. 09异步 Promise 与 async/await
  10. 10ES Module 与 npm 概览
  11. 11错误处理与调试
  12. 12综合练习与下一步

异步 Promise 与 async/await

回调、fetch、对接本站 API。

40 分钟

异步 Promise 与 async/await

这一课你将能做到

  1. 解释为什么网络请求不能「写在下一行就当成已经有结果」
  2. 用 async/await + try/catch 写一次取数流程
  3. 给界面加上「加载中 / 成功 / 失败」三种状态中的至少两种

为什么要学

fetch 去服务器拿数据需要时间。
若 JS 死等,页面就卡死——所以用异步:先继续干别的,结果以后再处理。

人话:你点了外卖(发起请求),先干别的事;外卖到了(Promise 完成)再吃饭(更新页面)。
本站前端调后端 API、任何真实产品拉列表,都建立在异步之上。

先建立图像

Promise(承诺)三种状态:
  pending   进行中
  fulfilled 成功,得到值
  rejected  失败,得到错误

async 函数里:
  await 某个 Promise → 等到结束再往下走
  (看起来像同步,仍不卡死浏览器主线程)
点击「加载」
  → 显示「加载中」
  → await fetch(...)
  → 成功:显示数据 / 失败:显示错误

跟着做

步骤 1:认识延迟与 await

function wait(ms) {
  // 返回一个「ms 毫秒后完成」的 Promise
  return new Promise(function (resolve) {
    setTimeout(resolve, ms);
  });
}

async function demo() {
  console.log("开始");
  await wait(500);
  console.log("0.5 秒后");
}
demo();
console.log("demo() 已调用,但这行可能先打印");

你应看到:demo() 已调用… 往往出现在「0.5 秒后」之前——说明异步不会堵住后面的同步代码。

步骤 2:忘记 await 会怎样(常见 bug)

async function f() {
  const p = wait(100); // 没 await
  console.log(p);      // Promise 对象,不是「结束之后的值」
  const done = await wait(100);
  console.log("await 之后:", done); // undefined(因 resolve 没传值)
}
f();

新手最常见问题:把 Promise 当成最终数据。看见 Promise { <pending> } 就要怀疑漏了 await。

步骤 3:假接口 + 界面三种状态

下面用公开测试接口;若网络不通可改成 await wait(500) 自己返回假数据。

保存为 js-async.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>异步</title>
</head>
<body>
  <button type="button" id="load">加载探测文本</button>
  <p id="out">尚未加载</p>
  <script>
    const out = document.getElementById("out");

    function wait(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }

    async function loadPoem() {
      out.textContent = "加载中…";
      try {
        const res = await fetch("https://www.cloudflare.com/cdn-cgi/trace");
        if (!res.ok) throw new Error("HTTP " + res.status);
        const text = await res.text();
        out.textContent = text.slice(0, 120) + "…";
      } catch (err) {
        out.textContent = "失败:" + err.message;
        console.error(err);
      }
    }

    document.getElementById("load").addEventListener("click", function () {
      loadPoem();
    });

    // 离线演练:把 loadPoem 换成下面这段即可
    async function loadFake() {
      out.textContent = "加载中…";
      try {
        await wait(500);
        out.textContent = "假数据:加载成功";
      } catch (err) {
        out.textContent = "失败:" + err.message;
      }
    }
  </script>
</body>
</html>

点击后应先见「加载中…」,再变为成功文本或失败信息。

步骤 4:then/catch 对照(能读即可)

wait(200)
  .then(function () {
    console.log("完成");
  })
  .catch(function (err) {
    console.error(err);
  });

async/await 是建立在 Promise 上的语法糖,读旧代码时能认 then 即可;新代码优先 async/await + try/catch。

步骤 5:串行与并行直觉

async function serial() {
  const a = await wait(200);
  const b = await wait(200); // 总共约 400ms
  console.log("串行结束");
}

async function parallel() {
  // 两个一起开始,再一起等
  await Promise.all([wait(200), wait(200)]); // 约 200ms
  console.log("并行结束");
}

serial();
parallel();

互相独立的请求用 Promise.all 往往更快;有依赖(第二个需要第一个的结果)才串行 await。

步骤 6:按钮防重复点击

let loading = false;
document.getElementById("load").addEventListener("click", async function () {
  if (loading) return;
  loading = true;
  this.disabled = true;
  try {
    await loadPoem();
  } finally {
    loading = false;
    this.disabled = false;
  }
});

异步进行中重复点击会叠请求。用 loading 标志或禁用按钮,是产品级细节的起点。

讲清楚要点

  1. 因为网络有延迟,所以用 Promise / async 表达「稍后才有结果」。
  2. 因为 await 只能写在 async 函数里(或特定顶层模块环境),所以入口常包一层 async function。
  3. 因为失败也会发生,所以用 try/catch(或 .catch),并给用户可读提示。
  4. 因为等待时用户需要反馈,所以UI 至少要有加载态与失败态。
  5. 因为漏写 await 会拿到 Promise,所以看见奇怪对象先检查 await。
  6. 因为独立请求可并行,所以合适时用 Promise.all,别无脑一路串行。

你可能会懵的地方

  • 异步是多线程吗?
    浏览器里 JS 主要仍单线程;靠事件循环安排「稍后继续」。先建立「稍后处理」直觉即可。

  • 为什么有时顺序乱?
    没 await 的异步会交叉。需要顺序就 await;要并行再 Promise.all。

  • CORS 报错
    浏览器安全策略拦跨域。学前端必遇;本课先保证你理解 await 流程,接口可换假数据。

  • res.json() 也要 await?
    要。fetch 完成只代表响应头大致可用;读 body(json/text)仍是异步。

错法对照

错法问题正解
不写 await拿到 Promiseawait 或 then
无加载态用户以为卡死「加载中」文案
空 catch吞掉错误至少 console + 用户提示
忽略 res.ok404 当成功解析非 ok 抛错或分支处理
连点发多数请求乱序回写界面loading 锁 / 禁用按钮

自测三问

  1. 用外卖比喻解释 Promise 的三种状态。
  2. 忘记 await 的典型现象是什么?
  3. 为什么异步流程也要 try/catch,并且界面要有加载态?

小结一句话

异步让等待网络时页面不卡死;用 async/await 写清顺序,处理加载与失败,并分清串行与并行。

延伸

  • MDN:使用 Promise
  • MDN:async 函数
  • 下一课:模块与 npm——代码如何拆文件。
加载本节练习…
← 上一课DOM 与事件下一课 →ES Module 与 npm 概览