网络异步请求和 async、await
理解 await 和异步网络请求,是掌握现代 JavaScript(以及 Python 的 asyncio 等高级特性)的绝对分水岭。
要弄懂它,我们不能只看代码,得先明白计算机在处理网络请求时到底面临什么问题。我们把这个过程拆解开来。
一、 为什么需要“异步”?(点餐的哲学)
假设你去快餐店点餐,有两种服务模式,这就对应了代码的两种执行方式:
- 同步模式(Synchronous - 阻塞):
你点了一个汉堡,然后你必须死死站在收银台前等 10 分钟。在这 10 分钟里,你不能玩手机,不能找座位,后面的顾客也不能点餐。整个餐厅(程序)因为等你这个汉堡(网络请求)被完全卡死(阻塞)了。 - 异步模式(Asynchronous - 非阻塞):
你点了一个汉堡,收银员给你一个取餐呼叫器(Promise)。然后你离开收银台去旁边找座位、刷手机(执行其他代码)。收银员也可以继续给后面的顾客点餐。等汉堡做好了,呼叫器一响(网络请求返回),你再去拿汉堡。
网络请求的本质,就是极度缓慢的 I/O(输入/输出)操作。
对于 CPU 来说,执行一行运算代码只需要几纳秒;但发送一个网络请求到 OpenAI 的服务器,再等服务器算完把结果传回来,可能需要好几秒。如果使用“同步模式”,整个程序在这几秒内会完全假死,用户的界面会卡住无法点击。因此,网络请求必须是异步的。
二、 异步网络请求的底层逻辑
当你执行之前那句调用大模型的代码时,底层发生了这样的事情:
- 交出任务: JavaScript 的主线程跑到这一行,发现这是一个网络请求,它自己不处理,而是把这个任务丢给底层的环境(比如浏览器的网络模块,或 Node.js 的 C++ 底层)。
- 获得凭证: 底层环境立刻丢给主线程一个“取餐呼叫器”。在编程语言里,这个呼叫器叫做
Promise(承诺)。它代表一个“目前还没结果,但未来某个时刻一定会给你个说法”的对象。 - 继续跑路: 主线程拿到 Promise 后,根本不留在原地等,而是立刻闪人,去执行后面的其他代码。
- 回调触发: 几秒钟后,底层的网络模块收到了 OpenAI 发来的数据,它会“按响呼叫器”。主线程在空闲时,就会接手这些数据,继续往下处理。
三、 await 语法的诞生与威力
在早期,为了处理那个“呼叫器响了之后该干嘛”的逻辑,程序员必须写出非常反人类的回调函数(Callback),或者冗长的 .then() 链式调用。
await 关键字的出现,被称为 JavaScript 异步编程的“终极语法糖”。 它让你可以用写同步代码的直观思维,来写异步代码。
await 的三大核心规则:
- JavaScript 发展过程中一个非常重要的新特性:顶层
await(Top-levelawait)。
在早期的 JavaScript 标准中,await必须严格包裹在一个由async关键字修饰的函数内部。如果在任何函数之外(即全局作用域/顶层)直接写await,程序会直接报语法错误(SyntaxError: await is only valid in async functions)。
从 Node.js v14.8.0+ 以及现代前端打包工具(如 Vite、Webpack 5)开始,JavaScript 官方引入了 **Top-level await** 特性,启用了 ES 模块(ES Modules)的顶层 await。
只要你的代码运行在 ES 模块环境 中,你就不再需要为了使用 await 而特意写一个用 async 包裹的匿名函数。
- 它的作用是“暂停”和“等待”:
当代码执行到await client.responses.create(...)时,这个async函数内部的执行会被瞬间暂停。await就像一个结界,它说:“在拿到网络请求的最终结果之前,这一行下面的代码谁也不许跑!” - 它不会阻塞主线程(最神奇的地方):
虽然当前函数被暂停了,但 JavaScript 会立刻跳出这个函数,去执行函数外面的其他代码。程序的整体运行没有被卡死!
四、 代码直观对比
我们来看看如果没有 await,获取大模型回答的代码长什么样:
【过去】使用 .then()(有些繁琐):
function getStory() {
// 发起请求,返回一个 Promise
client.responses.create({ input: "Write a story" })
.then(response => {
// 等请求成功后,这里的代码才执行
console.log(response.output_text);
});
console.log("这行代码会先打印出来!");
}
【现在】使用 async / await(极其清晰):
async function getStory() {
// 代码在这里暂停等待,但主线程没卡死
const response = await client.responses.create({ input: "Write a story" });
// 拿到结果后,直接往下走,就像普通变量赋值一样
console.log(response.output_text);
}
核心洞察:
await的本质,就是帮你自动把网络请求返回的那个Promise里的数据“拆包”出来,直接赋值给左边的变量。它剥离了复杂的异步流程,让代码阅读起来就是一条直观的“从上到下”的直线。

浙公网安备 33010602011771号