ES6 异步编程
请求数据、读写文件、定时任务都要异步,但异步是 JavaScript 里最绕也最重要的概念。
为什么需要异步
JS 是单线程语言,即一次只能做一件事,如果要做一件耗时的事(如请求接口)必须等它跑完才能继续,显示的就是页面卡死。
异步就是:先把耗时的任务丢出去,注册一个”完成后的回调”,主线程继续往下走,等任务完成再执行回调。
// 示例:setTimeout 就是最简单的异步
console.log('开始');
setTimeout(function () {
console.log('3 秒后执行');
}, 3000);
console.log('立刻执行');
// 输出:开始 → 立刻执行 →(3秒后)3 秒后执行
回调地狱
早期异步全靠回调。多个异步按顺序执行时,只能层层嵌套:
// ES5:回调层层嵌套(示意)
doA(function () {
doB(function () {
doC(function () {
console.log('全部完成');
});
});
});
问题:理解难、维护难、报错排查难,人称”回调地狱”。
嵌套的根源,在于回调是”传进去”的。函数要跑完内部逻辑,再在深处调用你给它的那个函数,层层传入就层层加深。
要打破嵌套,就得换一种思路,把结果从”往深处传”改成”往外返回”。即让异步函数自己返回一个带状态的对象,先给你一个”还没完成”的占位,等任务真正跑完后,再通过对象身上的方法把下一步接上去。ES6 的 Promise 正是这个对象。
Promise:把异步”链式化”
ES6 引入 Promise,用链式写法替代嵌套,把回调地狱”拍平”。
基本形态
// 示例:创建 Promise
const promise = new Promise(function (resolve, reject) {
// 模拟异步操作:1 秒后成功
setTimeout(function () {
resolve('数据拿到了'); // 成功时调用,并把结果"传出去"
// reject('出错了'); // 失败时调用
}, 1000);
});
resolve(成功)和 reject(失败)是 Promise 提供的两个函数,分别表示”成功/失败”。
消费结果:then / catch
promise
.then(function (data) { // resolve 时执行
console.log(data);
})
.catch(function (err) { // reject 时执行
console.log('出错了:', err);
});
链式调用:解决回调地狱
.then() 里返回另一个 Promise,可以继续 .then,形成”扁平的链”:
// 示例
getUser()
.then(function (user) {
return getOrders(user.id); // 返回新 Promise 继续链
})
.then(function (orders) {
console.log('订单', orders);
})
.catch(function (err) {
console.log('出错', err);
});
比起层层嵌套,链式明显更好读。这也真正解决了回调地狱。
async / await:异步代码像同步一样写
ES2017(属于现代 JS)推出的语法糖,让 Promise 写起来更直观:
// 示例:用 async/await 重写 Promise 链
async function getData() {
try {
const user = await getUser(); // 等它完成,拿到结果
const orders = await getOrders(user.id);
console.log(orders);
} catch (err) {
console.log('出错', err);
}
}
三条规则:
async标记的函数内部才能用await。await 表达式:暂停函数,等 Promise 出结果,返回值就是resolve的值。- 用
try/catch捕获reject的错误。
async 函数本身总是返回一个 Promise。
一个完整对比
// Promise 写法
function load() {
return getUser()
.then(function (u) { return getOrders(u.id); })
.then(function (o) { console.log(o); })
.catch(function (e) { console.log(e); });
}
// async/await 写法(推荐日常使用)
async function load() {
try {
const u = await getUser();
const o = await getOrders(u.id);
console.log(o);
} catch (e) {
console.log(e);
}
}
两种写法底层一样:async/await 只是 Promise 的”语法糖”。
三个异步 API 速览
日常写请求、文件时高频遇到:
| 形式 | 使用场景 |
|---|---|
Promise |
需要手动控制成功/失败 |
Promise.all([p1,p2]) |
多个异步并行执行,全完成才返回 |
async/await |
团队主要写法,最直观推荐 |
// 示例:Promise.all 并行请求
const [a, b] = await Promise.all([getA(), getB()]); // 同时发起,同时等
速查卡片
| 概念 | 作用 |
|---|---|
| 回调 | 异步执行完调用的函数 |
| 回调地狱 | 层层嵌套难维护 |
| Promise | 链式处理异步,then/catch |
| async/await | Promise 的语法糖,顺序更直观 |
try/catch |
捕获 async 里的错误 |
至此 ES6 的核心语法你已基本掌握。异步代码的演进(回调 → Promise → async/await)是通往工程化的一道重要关口,也是后续写请求、读写数据时每天都要用到的能力。