人人都能学编程
English 开始学习
核心 · 前端第 7 课 / 共 8 课免费阅读

异步与 fetch · 前端:HTML、CSS 与 JavaScript

网页怎么加载数据又不卡住?

课程目录

为什么需要它

加载用户资料或者一串消息,都要向服务器发请求然后等,有时一等就是几秒。如果网页在等的时候停住了,什么都点不了。JavaScript 的办法是异步代码:发出请求,继续做别的,等答案来了再处理。所有现代应用都建立在它之上。

直觉理解

任何需要时间的操作都会给你一个 Promise:一张「以后给你值」的欠条。

const res = await fetch("https://api.example.com/user/1");
const user = await res.json();
  • **fetch(url)** 发出一个 HTTP 请求,返回一个「响应」的 Promise。
  • **await 让这个函数暂停,直到 Promise 有结果,但不会卡住整个页面。它只能用在 async function** 里面。
  • 状态码在 200–299 时 res.ok 为 true;res.status 是状态码数字。fetch 遇到 404 或 500 不会抛出错误,所以要检查 res.ok。
  • res.json() 也是一个 Promise:同样要 await。
  • 用 **try { … } catch (err) { … }** 包起来,处理网络故障。

顺序很重要:先跑普通代码,再跑 Promise 的回调,最后才是定时器。

本课程里的 fetch 连的是一个模拟 API,所以你的代码每次拿到的答案都一样。

定义

Promise

An object standing for a value that isn't ready yet. It ends up fulfilled (with a value) or rejected (with an error).
代表一个还没准备好的值的对象。最后要么兑现(得到值),要么被拒绝(得到错误)。

async / await

async marks a function that can pause; await pauses it until a Promise settles and gives you the value.
async 标记一个可以暂停的函数;await 让它暂停到 Promise 有结果,然后把值交给你。

fetch

The browser's function for HTTP requests. It resolves with a response for any status; check res.ok yourself.
浏览器发 HTTP 请求的函数。不管状态码是多少,它都会给出一个响应;res.ok 要你自己检查。

示例

边等边做别的

在互动课里运行

setTimeout 把工作安排到以后;下一行马上就执行。

console.log("1: start");
setTimeout(() => console.log("3: timer done"), 1000);
console.log("2: carrying on");
输出
1: start
2: carrying on
3: timer done
逐步执行
  1. L2安排一个函数在 1 秒后运行,然后继续往下走。
  2. L3比定时器先运行,虽然它在代码里写得更靠后。

获取一个用户

在互动课里运行

await 响应,再 await 它的 JSON。

async function loadUser(id) {
  const res = await fetch(`https://api.example.com/user/${id}`);
  const user = await res.json();
  return user.name;
}

loadUser(1).then(name => console.log("Hello,", name));
console.log("waiting...");
输出
waiting...
Hello, Ada
逐步执行
  1. L2请求发出去了;这个函数在这里暂停。
  2. L3响应体是 JSON;res.json() 解析它(它也是异步的)。
  3. L7调用 async 函数会返回一个 Promise;完成时 .then 里的函数会运行。
  4. L8这一行先打印:没有任何东西在等 loadUser。

服务器说「不」的时候

在互动课里运行

404 也是一个响应。要检查 res.ok。

async function loadUser(id) {
  const res = await fetch(`https://api.example.com/user/${id}`);
  if (!res.ok) {
    return `Error ${res.status}`;
  }
  const user = await res.json();
  return user.name;
}

async function main() {
  console.log(await loadUser(2));
  console.log(await loadUser(99));
}
main();
输出
Linus
Error 404
逐步执行
  1. L3404 时 res.ok 是 false,所以在这里带着一条消息停下。
  2. L11main 里的 await 依次等待每个结果。

同时发两个请求

在互动课里运行

Promise.all 并行地等待多个 Promise。

async function nameOf(id) {
  const res = await fetch(`https://api.example.com/user/${id}`);
  const user = await res.json();
  return user.name;
}

async function main() {
  const names = await Promise.all([nameOf(1), nameOf(2)]);
  console.log(names);
}
main();
输出
[ 'Ada', 'Linus' ]
逐步执行
  1. L8两个请求同时开始;await 等到两个都有答案。

常见错误

忘了写 await

不写 await,拿到的是 Promise 而不是值:会出现 res.json is not a function,或者输出 Promise { <pending> }。

不检查 res.ok

只有网络故障时 fetch 才会拒绝。404 或 500 都会作为普通响应返回,响应体往往是错误信息,而不是你要的数据。

一个一个地等

await a(); await b(); 花的时间是两者之和。如果它们互不依赖,await Promise.all([a(), b()]) 只用花较慢那个的时间。

小测验

1. 这段代码会输出什么?

console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
console.log("D");
  1. A
    A
    D
    C
    B
  2. B
    A
    B
    C
    D
  3. C
    A
    D
    B
    C
查看答案

答案:A

先跑普通代码(A、D),再跑 Promise 回调(C),最后才是定时器(B),哪怕是 0 毫秒的定时器。

2. 服务器返回 404 Not Found。`await fetch(url)` 会怎样?

  1. A给出一个 res.ok === false 的响应
  2. B抛出一个错误
  3. C返回 null
查看答案

答案:A

fetch 只在网络出问题时才失败。HTTP 错误也是正常的响应:要检查 res.ok。

动手练习

3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。

  1. 预测输出事件的顺序

    按顺序准确写出它打印的内容。

  2. 改需求漏掉的 await

    getName(id) 应该返回用户的名字,现在却报错了。修好它。

  3. 从零写加载个人资料

    写一个 async function loadProfile(id),请求 https://api.example.com/user/<id>,返回:

    • 请求成功时,返回名字和城市,比如 "Ada (London)"
    • 响应不是 ok(比如 404)时,返回 "User not found"
    • fetch 本身失败时,返回 "Network error"(用 try/catch)

在互动课里练这一课

打开互动课,这些例子都能直接改、直接运行,练习题提交马上判题。免费开始,不用注册。

互动课按顺序解锁:前面的课还没学,会先带你到课程地图。

本课术语

术语中文英文定义
PromisePromiseA value that will be ready later, or an error.
async / awaitasync / awaitWrite asynchronous code that reads top to bottom.
fetchfetchThe browser function for HTTP requests.