一、什么是异步?
我们先来理解一个词:同步 vs 异步
- 同步(Synchronous) :代码一行一行执行,上一行没完成,下一行就不能动。
- 异步(Asynchronous) :代码可以跳过等待的部分,先去执行后面的,再回来处理。
举个例子
你打电话点外卖:
- 同步:你一直拿着电话等饭送来,你啥都不能干。
- 异步:你打完电话,继续做作业,饭来了再叫你。
二、为什么需要异步?
因为有些事情很慢,比如:
- 从服务器请求数据
- 读写文件
- 计时器(setTimeout)
- 网络请求(如
fetch)
如果这些事情是同步的,页面会卡住,用户体验很差。 所以 JavaScript 提供了异步机制。
三、常见的异步方式
1. setTimeout —— 最早的异步写法
console.log("1");
setTimeout(() => {
console.log("2");
}, 1000);
console.log("3");
输出顺序是:
1
3
2 // 延迟1秒后才执行
setTimeout会把函数“挂起来”,等时间到了再执行。
2. 回调函数(Callback)
function doAsync(callback) {
setTimeout(() => {
console.log("Async task done");
callback(); // 任务完成后执行回调
}, 1000);
}
doAsync(() => {
console.log("Callback executed");
});
缺点:回调地狱(callback hell) ,嵌套太多可读性差。
3. Promise —— 解决回调地狱
Promise 是一种“承诺”式的对象,告诉你任务将来会完成。
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("任务完成");
}, 1000);
});
promise.then(result => {
console.log(result); // 输出“任务完成”
});
resolve表示成功,reject表示失败。
4. async/await —— 最现代、最舒服的写法
async 和 await 是对 Promise 的语法糖,让异步代码看起来像同步。
function waitOneSecond() {
return new Promise(resolve => {
setTimeout(() => {
resolve("1秒后完成");
}, 1000);
});
}
async function main() {
console.log("开始");
let result = await waitOneSecond(); // 等待1秒
console.log(result);
console.log("结束");
}
main();
输出:
开始
(等1秒)
1秒后完成
结束
四、总结
| 方法 | 优点 | 缺点 |
|---|---|---|
| 回调函数 | 简单、基础 | 容易嵌套、难维护 |
| Promise | 避免回调地狱 | 链式写法较繁琐 |
| async/await | 最清晰、最直观 | 只适用于返回 Promise 的函数 |
一、Promise 是什么?
Promise 是 JavaScript 中的一种异步编程解决方案,它代表一个未来才会结束的操作(可能成功,也可能失败) 。
用一句话概括: 👉 Promise 是一个装着未来结果的盒子。
二、Promise 的三种状态
Promise 一旦创建,它的状态就会在以下三种之间切换:
- pending(等待中) :初始状态,还没有结果。
- fulfilled(已完成) :操作成功,得到了结果。
- rejected(已拒绝) :操作失败,返回错误。
状态一旦变成 fulfilled 或 rejected,就不能再变了!
三、Promise 的基本写法
创建 Promise
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作,比如网络请求
setTimeout(() => {
const success = true;
if (success) {
resolve("成功啦!");
} else {
reject("出错了!");
}
}, 1000);
});
resolve(value):代表成功,传出数据。reject(reason):代表失败,传出错误信息。
使用 .then() 和 .catch()
myPromise
.then(result => {
console.log("结果:", result);
})
.catch(error => {
console.error("错误:", error);
});
.then()是处理成功的回调.catch()是处理失败的回调
四、Promise 链式调用
.then() 本身也返回一个 Promise,可以继续 .then() 下去:
new Promise((resolve) => {
resolve(2);
})
.then((num) => {
console.log(num); // 2
return num * 2;
})
.then((num) => {
console.log(num); // 4
return num * 2;
})
.then((num) => {
console.log(num); // 8
});
每个 .then() 的返回值会传给下一个 .then()。
五、Promise 的静态方法
1. Promise.resolve(value)
快速创建一个成功状态的 Promise:
Promise.resolve("OK").then(res => {
console.log(res); // OK
});
2. Promise.reject(reason)
快速创建一个失败状态的 Promise:
Promise.reject("失败").catch(err => {
console.error(err); // 失败
});
3. Promise.all([])
多个 Promise 并发,全部成功才算成功:
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(res => {
console.log(res); // [1, 2, 3]
});
如果有一个失败,就会直接进入 catch。
4. Promise.race([])
谁先完成,就用谁的结果(最快的那个):
Promise.race([
new Promise(resolve => setTimeout(() => resolve("A"), 1000)),
new Promise(resolve => setTimeout(() => resolve("B"), 500))
]).then(res => {
console.log(res); // B
});
六、和 async/await 的关系
async/await 是 Promise 的语法糖,写起来更像同步:
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function run() {
console.log("等待 1 秒...");
await wait(1000);
console.log("时间到了!");
}
run();
所有
await后面跟的,都是一个 Promise。
七、常见错误和小提示
| 错误示例 | 原因 |
|---|---|
忘记 .catch() | 会导致错误没有被捕获 |
Promise 嵌套写太多 .then() | 可读性差,建议用 async/await |
reject() 不代表抛出异常 | 它只是把 Promise 状态变成失败,catch 才能捕捉 |
用了 async/await 却没 try/catch | 一旦出错,程序会崩溃 |
八、一个完整的小例子
function fakeRequest(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (data === "hello") {
resolve("返回成功数据");
} else {
reject("出错啦!");
}
}, 1000);
});
}
fakeRequest("hello")
.then(res => {
console.log("成功:", res);
})
.catch(err => {
console.error("失败:", err);
});
如果你愿意,我可以给你设计几个小练习题来巩固 Promise 的用法。你想试试吗?
一、async/await 是什么?
它是对 Promise 的一种更清晰、更优雅的写法方式。
async:声明一个函数为异步函数await:只能在异步函数内部使用,表示等待 Promise 执行完再继续
📌 本质:async/await 是 Promise 的语法糖(语法更好看,本质还是 Promise)
二、为什么要用 async/await?
用 .then().then().catch() 写 Promise,很容易写出“回调地狱”:
doA()
.then(res => doB(res))
.then(res => doC(res))
.catch(err => console.error(err));
但用 async/await 就很清晰:
async function main() {
try {
const a = await doA();
const b = await doB(a);
const c = await doC(b);
console.log(c);
} catch (err) {
console.error(err);
}
}
main();
三、基本语法详解
1. 声明一个异步函数
async function hello() {
return "你好,世界";
}
等价于:
function hello() {
return Promise.resolve("你好,世界");
}
所以:async 函数一定返回一个 Promise!
2. 使用 await 等待 Promise 结果
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sayHi() {
console.log("开始等待");
await delay(1000);
console.log("1秒过后");
}
sayHi();
执行结果:
开始等待
(等待1秒)
1秒过后
📌 await 会“暂停”这个函数的执行,直到 Promise 完成,再继续往下走。
函数前的关键字
await使函数等待 promise。
关键字await让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。
await关键字只能在async函数中使用。
四、处理错误(try...catch)
在 await 过程中,如果 Promise 被 reject 了,就会抛出异常。
你需要用 try...catch 把它包住,防止程序崩掉:
async function fetchData() {
try {
const res = await fetch("https://api.example.com/data");
const json = await res.json();
console.log(json);
} catch (err) {
console.error("请求出错:", err);
}
}
五、常见组合用法
1. 多个 await 顺序执行(串行)
const a = await task1();
const b = await task2(a);
const c = await task3(b);
每一步都要等前一步完成。
2. 并行执行(更快!)
const [res1, res2] = await Promise.all([
fetchData1(),
fetchData2()
]);
多个任务一起开始执行,全部完成后再拿结果。
六、async/await 常见问题
| 问题 | 说明 |
|---|---|
await 外面不能用 | await 必须写在 async 函数里面 |
忘记 try/catch | 一旦出错,整个函数会抛异常 |
| 不会自动等待多个异步 | 需要 Promise.all() 来并发等待 |
| async 函数默认返回 Promise | 不是“立即返回值”,而是“承诺返回值” |
七、一个真实例子:顺序执行任务
function wait(ms, msg) {
return new Promise(resolve => {
setTimeout(() => {
console.log(msg);
resolve();
}, ms);
});
}
async function runTasks() {
await wait(1000, "任务1完成");
await wait(500, "任务2完成");
await wait(200, "任务3完成");
console.log("全部完成");
}
runTasks();
输出:
(1秒后)任务1完成
(0.5秒后)任务2完成
(0.2秒后)任务3完成
全部完成
八、总结
| 特点 | 说明 |
|---|---|
| 更清晰 | 写起来像同步代码 |
| 更易维护 | 逻辑清晰,错误更好捕获 |
| 本质是 Promise | async 函数返回 Promise,await 等待 Promise |
函数前面的关键字 async 有两个作用:
- 让这个函数总是返回一个 promise。
- 允许在该函数内使用
await。
Promise 前的关键字 await 使 JavaScript 引擎等待该 promise settle,然后:
- 如果有 error,就会抛出异常 —— 就像那里调用了
throw error一样。 - 否则,就返回结果。
这两个关键字一起提供了一个很好的用来编写异步代码的框架,这种代码易于阅读也易于编写。
有了 async/await 之后,我们就几乎不需要使用 promise.then/catch,但是不要忘了它们是基于 promise 的,因为有些时候(例如在最外层作用域)我们不得不使用这些方法。并且,当我们需要同时等待需要任务时,Promise.all 是很好用的。