[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| Promise | n. | /ˈprɒmɪs/ | 表示"将来才给结果"的容器对象,用 then 或 await 等它兑现 |
| async | adj. | /əˈsɪŋk/ | 异步关键词,修饰函数后该函数总是返回一个 Promise |
| await | v. | /əˈweɪt/ | 等待右边的 Promise 完成再执行下一步,把异步写得像同步 |
| then | v. | /ðen/ | Promise 的方法,用来接收内部 resolve 交出来的结果 |
| resolve | v. | /rɪˈzɒlv/ | Promise 内部的方法,负责把任务结果"交出去" |
| setInterval | v. | /set ˈɪntəvl/ | 定时器,每隔固定毫秒执行一次回调,本节用它体验异步并行 |
| try/catch | n. | /traɪ kætʃ/ | 异常捕获结构,配合 await 处理异步任务的失败 |
Lecture03-异步与 Promise(async - await)
1、本节讲什么
搞懂 JS 异步的核心工具 Promise,以及 async / await 三个关键词各自干什么、怎么配合用。全程用 node 运行 .js 文件,不涉及网络请求(真正的请求后端,将来第 23 节直接用 axios)。
2、概念速览
什么是异步任务:有些操作要"等别人"(等网络、等定时器),等的时候不能把程序卡死。异步 = 我先去干别的,结果好了再通知我。
什么是 Promise:一个"将来才给结果"的容器。你拿到的不是结果本身,而是"会出结果的凭证",用 then 或 await 等它兑现。
类比 Java:Promise 类似 CompletableFuture——都是"异步结果"的封装。
正确 vs 误区:异步不是"更快",而是"不用等";不 await 就想拿结果,拿到的是"还没完成的 Promise"。
3、先看效果
Promise 能"把要异步执行的任务装进去",任务做完能把结果交出来。建 promise-then.js,node promise-then.js:
const promise = new Promise((resolve) => {
const result = 10 + 5; // Promise 内的任务:算个加法
resolve(result); // 把结果"交出去"
});
promise.then((taskResult) => {
console.log("内部打印:taskResult = " + taskResult); // 15
});
终端打印 内部打印:taskResult = 15——这就是"Promise 里的结果,能在外面接到"。
4、照着做(4 个文件,一步步来)
① 体验异步:Promise 内外并行执行
Promise 最基本的作用:把一段代码放进去,让它和外面并行执行(谁也不等谁)。建 promise-async.js,node promise-async.js:
// 初步体验 Promise 封装异步操作
// 异步:两边谁都不用等谁,同时并行、交错执行
// 同步:A 操作需要等 B 操作执行完,才能执行自己的操作
// 1、在 Promise 外面(主线程)每隔 1 秒打印一句话
setInterval(() => console.log("●主线程~~~" + Math.random() * 100), 1000);
// 2、在 Promise 里面(子线程)每隔 1 秒打印一句话
new Promise(() => {
setInterval(() => console.log("○子线程~~~" + Math.random() * 100), 1000);
});
你会看到 ● 和 ○ 两行交错出现、互不等——这就是"异步":两边同时进行。
② Promise 把结果返回给调用者(then)
光并行不够,我们还要能拿到 Promise 里算出来的结果。resolve(结果) 负责"把结果交出去",then 负责"在外面接住"。建 promise-then.js,node promise-then.js:
// 总体说明:Promise 执行任务之后,会有一个最终的结果,可以返回给调用者
// 设定:Promise 要执行的任务就是 10 和 5 相加
let taskResultOutter = 0;
const promise = new Promise((resolve) => {
// 1、Promise 内执行数据计算,得到结果
const result = 10 + 5;
// 2、把结果返回给外部调用者
resolve(result);
});
// 提问:为啥要舍近求远,在 then() 里面接收任务结果?
// 回答:因为我们需要模拟将来使用框架的场景,Promise 内部的代码在框架内部我们摸不到,但是可以获取到 Promise 对象
// 模拟:Promise 里面的任务将来就是 Axios 给后端发送请求;任务结果就是后端返回的响应
promise.then((taskResult) => {
console.log("内部打印:taskResult = " + taskResult);
taskResultOutter = taskResult;
});
// 外部打印 taskResultOutter 是在子线程赋值操作之前执行的
// 原因是外部主线程和内部子线程是异步执行的,外部不会等内部完成赋值
// 所以从效果上,我们看到外部变量仍然是初始值,没有被赋值
console.log("外部打印:taskResultOutter = " + taskResultOutter);
两个要点:
- 结果靠
then接收,不靠外部变量——所以下面外部变量taskResultOutter打印出来还是 0(异步执行,外部不等人)。 - 为什么"舍近求远"用 then 接? 因为将来用框架时,Promise 内部的代码我们摸不到,只能拿到 Promise 对象(比如 axios 发请求,任务结果就是后端响应),所以必须通过
then拿。
③ await:把异步变成同步
then 接结果还是有点绕,await 更直接:等右边 Promise 做完,再执行下一步——把异步"翻译"成同步的样子,逻辑更顺、代码更优雅。建 promise-await.js,node promise-await.js:
// 在 Promise 中封装子任务
// await 关键词的作用:等右边的 Promise 对象把子任务执行完,然后再执行下一步操作
// 效果:把异步任务转换为了同步任务
// 好处1:逻辑方面更加顺畅
// 好处2:代码变得更加优雅
const taskResult = await new Promise((resolve) => {
setTimeout(() => {
console.log("出门之前先化个妆~~~[子任务]");
resolve("任务执行的结果~~~[子任务]");
}, 3000);
});
// 获取到任务执行的结果之后,打印出来
console.log("[外部主线程]taskResult = " + taskResult);
你会看到:先打印"化妆"(3 秒后),再打印结果——await 让外部停下来等子任务完成。这段 await 写在模块顶层(Vite 项目的 .js 默认就是模块,顶层可以用 await);如果 await 写在函数内部,这个函数必须 async——看下一步。
④ 函数内用 await,函数必须 async
规则一句话:一个函数内部用了 await,函数本身就必须用 async 修饰。建 promise-async.js,node promise-async.js:
// 一个函数内部使用了 await 关键词,那么函数本身就必须使用 async 修饰
async function love() {
const value = await new Promise((resolve) => {
resolve("task result ...");
});
console.log("value = " + value);
}
love();
把 async 去掉试试——node 直接报语法错误。为什么必须,见下面的补充。
补充:为什么 await 必须配 async?

一句话:await 是"函数内部的暂停点",async 是"给整个函数装上可等待外壳"——暂停点要成立,前提是外壳先存在。
-
await= 函数在这里"等一个将来的结果",等于让函数在某处暂停。 -
一暂停就引出两个问题:
- 函数结果要等一会儿才出来;
- 调用方必须知道要等,否则会把"没完成的 Promise"当"完成的值"用。
-
async一次性解决:声明后这个函数总是返回一个 Promise,调用方就该await/.then拿结果、try/catch/.catch接错误。
| 普通函数 | async 函数 |
|---|---|
| 调用直接返回"值" | 调用返回 Promise(将来完成信号) |
内部不能 await(写了就报错) | 内部能 await |
| 调用方直接拿结果 | 调用方必须 await / .then |
| 错误靠外层接 | 错误靠 try/catch / .catch 接 |
比喻:await = 车在某站停下来等人;async = 这辆车整体被登记成"异步班车、到终点会通知你"。你要停下来等人,前提是车本身是异步班车且有完成信号;普通车停下来等人,没人登记、没人知道、没处通知,所以 JS 直接不让写。
一句话记法:await 是"我停下来等别人",async 是"我这个函数整体也是个要等的异步任务、并对外发完成信号"——后者是前者的容器。
5、常见疑问
- Q:
await到底在等什么? A:等一个Promise有结果。类似 Java 里CompletableFuture.get()。await只能写在async函数里(或模块顶层)。 - Q:什么时候用
.then,什么时候用await? A:都能用,但await更像同步代码、更好读,本教程后面一律用await。.then你能看懂就行。 - Q:Promise 里的结果,外部变量为什么拿不到(还是 0)?
A:因为异步:外部主线程不会等内部子线程赋值。要拿结果就用
then接收或await,别靠外部变量。 - Q:
.catch和try/catch区别? A:.catch是 Promise 自带的错误处理;await写法配合try/catch捕获错误。推荐await + try/catch:
async function run() {
try {
const msg = await wait
console.log(msg)
} catch (err) {
console.log('出错了:', err) // 失败一定处理,别静默
}
}
6、规范要点
- 异步结果用
then/await接收,不要靠外部变量(靠外部变量接,拿到的还是初始值)。 - 异步逻辑一定要处理失败(
try/catch或.catch),别静默失败难排查。 - 将来写异步请求时,记住"先
await拿结果,再console.log确认数据长什么样,再上页面"。
7、本节验证
- 能用
new Promise((resolve) => { ... })封装一段异步任务并resolve结果 ✅ - 能用
.then()或await接到 Promise 里的结果,知道不能靠外部变量接 ✅ - 明白
await只能写在async函数里(或模块顶层),去掉async会报语法错误 ✅ - 能说清
await与async的分工:一个是"暂停等待点"、一个是"可等待外壳" ✅ - 知道异步失败要用
try/catch或.catch处理,别静默失败 ✅