JavaScript 异步编程实战指南:从回调到 Promise 与生成器
2025-07-08 07:38:46作者:郦嵘贵Just
前言
在现代 JavaScript 开发中,异步编程是每个开发者必须掌握的核心技能。本文将深入探讨 JavaScript 异步编程的各种技术实现,帮助开发者从基础到进阶全面理解异步编程范式。
1. 顺序执行异步代码块
最基本的异步控制模式是确保多个异步操作按顺序执行。我们可以通过回调嵌套实现:
function asyncFunc() {
console.log("开始 asyncFunc1");
setTimeout(() => {
console.log("完成 asyncFunc1");
console.log("开始 asyncFunc2");
setTimeout(() => {
console.log("完成 asyncFunc2");
console.log("开始 asyncFunc3");
setTimeout(() => {
console.log("完成 asyncFunc3");
}, 1000);
}, 2000);
}, 3000);
}
这种模式虽然直观,但会导致"回调地狱",代码难以维护。接下来我们将看到更好的解决方案。
2. XMLHttpRequest 基础
传统 AJAX 请求使用 XMLHttpRequest 对象:
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function() {
console.log(this.response);
};
xhr.onerror = function() {
console.log(this.statusText);
};
xhr.send();
XHR 提供了基本的异步请求能力,但现代开发中已被更先进的 API 取代。
3. Promise 基础
Promise 是现代 JavaScript 异步编程的核心:
function asyncResolveFunc() {
return new Promise((resolve) => resolve("成功"));
}
function asyncRejectFunc() {
return new Promise((_, reject) => reject("失败"));
}
const promiseSuccess = asyncResolveFunc();
const promiseFailure = asyncRejectFunc();
promiseSuccess.then(
(data) => console.log(data),
(err) => console.log(err)
);
promiseFailure.then(
(data) => console.log(data),
(err) => console.log(err))
);
Promise 有三种状态:pending、fulfilled 和 rejected,状态一旦确定就不可更改。
4. 将 setTimeout 包装为 Promise
我们可以将回调式 API 转换为 Promise:
function setTimeoutPromise(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
console.log("任务开始");
setTimeoutPromise(3000).then(() => console.log("任务完成"));
这种模式极大提高了代码可读性。
5. 基于 Promise 的 XHR 封装
将 XHR 封装为 Promise 使代码更清晰:
function fetchData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function() {
this.status === 200 ? resolve(this) : reject(this);
};
xhr.onerror = () => reject(this);
xhr.send();
});
}
6. 使用 Fetch API
现代浏览器提供了更简洁的 Fetch API:
fetch("https://reqbin.com/echo/get/json", {
method: "GET",
headers: { "Content-Type": "application/json" }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.log(err));
7. 取消 Fetch 请求
使用 AbortController 可以取消请求:
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(response => console.log(response))
.catch(err => console.warn(err));
controller.abort();
8. async/await 语法
async/await 让异步代码看起来像同步代码:
async function asyncAwaitFunc() {
try {
console.log("正常执行");
await promiseReturningFunc();
console.log("Promise 解决后继续执行");
} catch (err) {
console.log("错误: " + err);
}
}
9. Promise.all 批量处理
并行执行多个 Promise,全部成功或任一失败:
const promises = [async1(), async2(), async3()];
// 使用 then/catch
Promise.all(promises)
.then(results => results.forEach(data => console.log(data)))
.catch(err => console.log(err));
// 使用 async/await
(async () => {
try {
const results = await Promise.all(promises);
results.forEach(data => console.log(data));
} catch (err) {
console.log(err);
}
})();
10. Promise.allSettled 处理所有结果
无论成功失败都等待所有 Promise 完成:
Promise.allSettled(promises).then(results => {
results.forEach(result => {
if (result.status === "fulfilled") {
console.log("成功:", result.value);
} else {
console.log("失败:", result.reason);
}
});
});
11. Promise.race 竞速
第一个完成的 Promise 决定结果:
Promise.race([
new Promise(resolve => setTimeout(() => resolve("A"), 2000)),
new Promise((_, reject) => setTimeout(() => reject("B"), 1000))
])
.then(console.log)
.catch(console.log); // 输出 B
12. 生成器函数基础
生成器可以暂停和恢复函数执行:
function* generatorFunc() {
const a = yield;
const b = yield;
return a + b;
}
const it = generatorFunc();
it.next(); // { value: undefined, done: false }
it.next(3); // { value: undefined, done: false }
const sum = it.next(5); // { value: 8, done: true }
console.log(sum.value); // 8
13. 生成器组合
生成器可以嵌套使用:
function* gen1() {
yield 1;
yield* gen2();
yield 4;
}
function* gen2() {
yield 2;
yield 3;
}
for (const value of gen1()) {
console.log(value); // 输出 1, 2, 3, 4
}
14. 实现简易 Promise
理解 Promise 的工作原理:
function MyPromise(resolver) {
let successCallbacks = [];
let failureCallbacks = [];
let state = "pending";
let value;
function resolve(val) {
if (state === "pending") {
state = "fulfilled";
value = val;
successCallbacks.forEach(cb => cb(value));
}
}
function reject(reason) {
if (state === "pending") {
state = "rejected";
value = reason;
failureCallbacks.forEach(cb => cb(value));
}
}
setTimeout(() => resolver(resolve, reject), 0);
return {
then: function(onFulfilled, onRejected) {
if (state === "fulfilled") {
onFulfilled(value);
} else if (state === "rejected") {
onRejected(value);
} else {
successCallbacks.push(onFulfilled);
failureCallbacks.push(onRejected);
}
}
};
}
15. 实现 Promise.all
function PromiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;
promises.forEach((promise, index) => {
promise.then(
value => {
results[index] = value;
if (++completed === promises.length) {
resolve(results);
}
},
reason => reject(reason)
);
});
});
}
16. 生成器与 Promise 结合
实现类似 async/await 的效果:
function asyncTask(data, delay) {
return new Promise(resolve => setTimeout(() => resolve(data), delay));
}
function* main() {
const a = yield asyncTask(2, 1000);
const b = yield asyncTask(3, 1500);
console.log(a + b); // 5
}
function runGenerator(gen) {
const it = gen();
function handle(result) {
if (result.done) return;
result.value.then(
data => handle(it.next(data)),
err => it.throw(err)
);
}
handle(it.next());
}
runGenerator(main);
总结
本文全面介绍了 JavaScript 异步编程的各种技术,从基础的回调函数到 Promise、async/await 和生成器。掌握这些技术对于现代 JavaScript 开发至关重要,能够帮助开发者编写更清晰、更易维护的异步代码。