【JavaScript】异步编程
个人主页:Guiat
归属专栏:HTML CSS JavaScript
文章目录
- 1. 异步编程基础
- 1.1 同步与异步
- 1.1.1 同步执行
- 1.1.2 异步执行
- 1.2 JavaScript 事件循环
- 2. 回调函数
- 2.1 基本回调模式
- 2.2 错误处理
- 2.3 回调地狱
- 3. Promise
- 3.1 Promise 基础
- 3.2 Promise 链式调用
- 3.3 Promise 组合
- 4. Async/Await
- 4.1 基本语法
- 4.2 串行与并行执行
- 4.3 处理循环中的异步操作
- 5. 异步迭代器与生成器
- 5.1 异步迭代器
- 5.2 异步生成器
- 6. 实际应用场景
- 6.1 API 请求与错误重试
- 6.2 异步数据加载与缓存
- 6.3 异步任务队列
- 6.4 异步状态管理
正文
1. 异步编程基础
JavaScript 是单线程语言,异步编程是处理非阻塞操作的关键机制。
1.1 同步与异步
1.1.1 同步执行
所有操作按顺序执行,后续任务必须等待前一个任务完成。
console.log("任务 1");
console.log("任务 2");
console.log("任务 3");
// 输出顺序: 任务 1, 任务 2, 任务 3
1.1.2 异步执行
允许某些操作在后台进行,不阻塞主线程执行。
console.log("任务 1");
setTimeout(() => {
console.log("任务 2"); // 异步任务
}, 1000);
console.log("任务 3");
// 输出顺序: 任务 1, 任务 3, 任务 2
1.2 JavaScript 事件循环
JavaScript 通过事件循环处理异步操作,包含以下关键组件:
- 调用栈(Call Stack):执行同步代码
- 任务队列(Task Queue):存放待执行的回调函数
- 微任务队列(Microtask Queue):优先级高于宏任务队列的队列
- 事件循环(Event Loop):不断检查调用栈是否为空,并将任务从队列移入栈中
console.log("开始");
setTimeout(() => {
console.log("宏任务 (setTimeout)");
}, 0);
Promise.resolve().then(() => {
console.log("微任务 (Promise)");
});
console.log("结束");
// 输出顺序: 开始, 结束, 微任务 (Promise), 宏任务 (setTimeout)
2. 回调函数
回调函数是异步编程的基础,是传递给另一个函数的函数,在特定事件或条件满足时被执行。
2.1 基本回调模式
function fetchData(callback) {
// 模拟网络请求
setTimeout(() => {
const data = { name: "John", age: 30 };
callback(data);
}, 1000);
}
// 使用回调获取数据
fetchData(function(data) {
console.log("获取的数据:", data);
});
console.log("请求已发送,等待数据...");
2.2 错误处理
回调函数通常采用 “错误优先” 的模式进行错误处理。
function fetchData(callback) {
setTimeout(() => {
// 模拟随机错误
const success = Math.random() > 0.3;
if (success) {
callback(null, { name: "John", age: 30 });
} else {
callback(new Error("无法获取数据"), null);
}
}, 1000);
}
// 使用错误优先回调
fetchData(function(error, data) {
if (error) {
console.error("发生错误:", error.message);
return;
}
console.log("获取的数据:", data);
});
2.3 回调地狱
嵌套回调会导致代码可读性差,难以维护,俗称"回调地狱"。
// 回调地狱示例
getUser(function(user) {
getProfile(user.id, function(profile) {
getFriends(profile.id, function(friends) {
getPhotos(friends[0].id, function(photos) {
console.log(photos);
// 更多嵌套回调...
}, function(error) {
console.error("获取照片失败:", error);
});
}, function(error) {
console.error("获取好友失败:", error);
});
}, function(error) {
console.error("获取资料失败:", error);
});
}, function(error) {
console.error("获取用户失败:", error);
});
3. Promise
Promise 是异步编程的一种解决方案,用于表示一个异步操作的最终完成(或失败)及其结果值。
3.1 Promise 基础
Promise 有三种状态:
- Pending(进行中)
- Fulfilled(已成功)
- Rejected(已失败)
// 创建一个 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.3;
if (success) {
resolve("操作成功"); // 成功时调用
} else {
reject(new Error("操作失败")); // 失败时调用
}
}, 1000);
});
// 处理 Promise 结果
promise
.then(result => {
console.log(result); // 成功时执行
return "处理后的数据";
})
.then(data => {
console.log("链式调用:", data);
})
.catch(error => {
console.error("错误处理:", error.message); // 失败时执行
})
.finally(() => {
console.log("无论成功或失败都会执行"); // 总是执行
});
3.2 Promise 链式调用
Promise 可以链式调用,避免回调地狱。
// 使用 Promise 重构回调地狱
function getUser(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ id: userId, name: "John" }), 1000);
});
}
function getProfile(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ id: userId, hobbies: ["reading", "music"] }), 1000);
});
}
function getFriends(profileId) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve([{ id: 101, name: "Alice" }, { id: 102, name: "Bob" }]), 1000);
});
}
// 链式调用
getUser(1)
.then(user => {
console.log("用户:", user);
return getProfile(user.id);
})
.then(profile => {
console.log("个人资料:", profile);
return getFriends(profile.id);
})
.then(friends => {
console.log("好友列表:", friends);
})
.catch(error => {
console.error("发生错误:", error);
});
3.3 Promise 组合
Promise 提供了多种方法来组合处理多个 Promise。
// Promise.all - 等待所有 Promise 完成
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(([users, posts, comments]) => {
console.log("所有数据:", users, posts, comments);
})
.catch(error => {
console.error("至少一个请求失败:", error);
});
// Promise.race - 返回最先完成的 Promise 结果
Promise.race([
new Promise(resolve => setTimeout(() => resolve("快速请求"), 1000)),
new Promise(resolve => setTimeout(() => resolve("慢速请求"), 3000))
])
.then(result => {
console.log("最先完成的是:", result); // "快速请求"
});
// Promise.allSettled - 等待所有 Promise 完成,无论成功或失败
Promise.allSettled([
Promise.resolve("成功 1"),
Promise.reject(new Error("失败")),
Promise.resolve("成功 2")
])
.then(results => {
console.log(results);
// [{status: "fulfilled", value: "成功 1"},
// {status: "rejected", reason: Error: "失败"},
// {status: "fulfilled", value: "成功 2"}]
});
// Promise.any - 返回第一个成功的 Promise (ES2021)
Promise.any([
new Promise((_, reject) => setTimeout(() => reject(new Error("失败 1")), 1000)),
new Promise(resolve => setTimeout(() => resolve("成功"), 2000)),
new Promise((_, reject) => setTimeout(() => reject(new Error("失败 2")), 3000))
])
.then(result => {
console.log("第一个成功的结果:", result); // "成功"
})
.catch(error => {
console.error("所有 Promise 都失败了:", error);
});
4. Async/Await
Async/Await 是基于 Promise 的语法糖,使异步代码看起来更像同步代码。
4.1 基本语法
// 定义异步函数
async function fetchUserData() {
try {
// await 暂停函数执行,直到 Promise 解决
const response = await fetch('https://api.example.com/users');
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP 错误:${response.status}`);
}
// 解析 JSON 数据
const data = await response.json();
console.log("用户数据:", data);
return data;
} catch (error) {
console.error("获取数据失败:", error.message);
throw error; // 重新抛出错误
}
}
// 调用异步函数
fetchUserData()
.then(data => console.log("处理数据:", data))
.catch(error => console.error("外部错误处理:", error));
// 或者使用自执行异步函数
(async () => {
try {
const data = await fetchUserData();
console.log("处理数据:", data);
} catch (error) {
console.error("外部错误处理:", error);
}
})();
4.2 串行与并行执行
// 串行执行 - 顺序等待每个请求完成
async function serialFetch() {
console.time('serial');
const userResponse = await fetch('/api/users');
const users = await userResponse.json();
const postsResponse = await fetch('/api/posts');
const posts = await postsResponse.json();
const commentsResponse = await fetch('/api/comments');
const comments = await commentsResponse.json();
console.timeEnd('serial');
return { users, posts, comments };
}
// 并行执行 - 同时发起所有请求
async function parallelFetch() {
console.time('parallel');
// 同时发起所有请求
const [userResponse, postsResponse, commentsResponse] = await Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]);
// 并行解析 JSON
const [users, posts, comments] = await Promise.all([
userResponse.json(),
postsResponse.json(),
commentsResponse.json()
]);
console.timeEnd('parallel');
return { users, posts, comments };
}
4.3 处理循环中的异步操作
// 一次处理所有项目 (并行)
async function processItemsParallel(items) {
const promises = items.map(item => processItem(item));
const results = await Promise.all(promises);
return results;
}
// 按顺序处理每一项 (串行)
async function processItemsSequential(items) {
const results = [];
for (const item of items) {
// 等待每一项完成后再处理下一项
const result = await processItem(item);
results.push(result);
}
return results;
}
// 限制并发数的并行处理
async function processItemsWithConcurrencyLimit(items, limit = 3) {
const results = [];
const running = [];
for (const item of items) {
// 创建处理每项的 Promise
const promise = processItem(item)
.then(result => {
// 从运行列表中移除完成的 Promise
running.splice(running.indexOf(promise), 1);
return result;
});
results.push(promise);
running.push(promise);
// 如果达到并发限制,等待一个任务完成
if (running.length >= limit) {
await Promise.race(running);
}
}
// 等待所有任务完成
return Promise.all(results);
}
5. 异步迭代器与生成器
5.1 异步迭代器
ES2018 引入了异步迭代器,可以使用 for await...of
遍历异步数据源。
// 创建一个异步可迭代对象
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i < 5) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
return { value: i++, done: false };
}
return { done: true };
}
};
}
};
// 使用 for await...of 遍历
async function iterateAsyncIterable() {
console.log("开始迭代");
for await (const value of asyncIterable) {
console.log("值:", value);
}
console.log("迭代完成");
}
iterateAsyncIterable();
// 输出:
// 开始迭代
// 值: 0 (等待1秒后)
// 值: 1 (再等待1秒)
// ...
// 迭代完成 (总共等待5秒)
5.2 异步生成器
异步生成器结合了生成器和异步函数的特性。
// 定义异步生成器函数
async function* createAsyncGenerator() {
for (let i = 0; i < 5; i++) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
// 使用异步生成器
async function useAsyncGenerator() {
const asyncGenerator = createAsyncGenerator();
for await (const value of asyncGenerator) {
console.log("值:", value);
}
}
useAsyncGenerator();
// 手动迭代异步生成器
async function manualIteration() {
const asyncGenerator = createAsyncGenerator();
// 首次调用 next()
let result = await asyncGenerator.next();
while (!result.done) {
console.log("手动迭代值:", result.value);
// 获取下一个值
result = await asyncGenerator.next();
}
}
6. 实际应用场景
6.1 API 请求与错误重试
async function fetchWithRetry(url, options = {}, retries = 3, backoff = 300) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (retries <= 0) {
throw error;
}
console.warn(`请求失败,等待 ${backoff}ms 后重试...`, error.message);
// 等待指定时间
await new Promise(resolve => setTimeout(resolve, backoff));
// 递归调用,减少重试次数,增加等待时间
return fetchWithRetry(url, options, retries - 1, backoff * 2);
}
}
// 使用
fetchWithRetry('https://api.example.com/data')
.then(data => console.log('成功获取数据:', data))
.catch(error => console.error('所有重试都失败:', error));
6.2 异步数据加载与缓存
// 简单的异步缓存系统
class AsyncCache {
constructor(ttl = 60000) { // 默认缓存时间 60 秒
this.cache = new Map();
this.ttl = ttl;
}
async get(key, fetchFn) {
const now = Date.now();
const cached = this.cache.get(key);
// 如果存在有效的缓存项,直接返回
if (cached && now < cached.expiry) {
console.log(`Cache hit for key: ${key}`);
return cached.data;
}
console.log(`Cache miss for key: ${key}, fetching...`);
try {
// 获取新数据
const data = await fetchFn();
// 存入缓存
this.cache.set(key, {
data,
expiry: now + this.ttl
});
return data;
} catch (error) {
// 如果获取失败但存在过期缓存,返回过期数据
if (cached) {
console.warn(`Fetch failed, using stale data for: ${key}`);
return cached.data;
}
throw error;
}
}
invalidate(key) {
this.cache.delete(key);
}
clear() {
this.cache.clear();
}
}
// 使用示例
const dataCache = new AsyncCache(5 * 60 * 1000); // 5分钟缓存
async function getUserData(userId) {
return await dataCache.get(`user:${userId}`, async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
});
}
6.3 异步任务队列
class AsyncTaskQueue {
constructor(concurrency = 2) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(task) {
return new Promise((resolve, reject) => {
// 将任务添加到队列
this.queue.push({
task,
resolve,
reject
});
// 尝试执行下一个任务
this.runNext();
});
}
async runNext() {
// 如果已达到并发限制或队列为空,则返回
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
// 从队列取出一个任务
const { task, resolve, reject } = this.queue.shift();
this.running++;
try {
// 执行任务
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
// 任务完成,减少计数
this.running--;
// 尝试执行下一个任务
this.runNext();
}
}
}
// 使用示例
const queue = new AsyncTaskQueue(3); // 最多3个并发任务
async function processFiles(files) {
const results = [];
for (const file of files) {
// 添加异步任务到队列
const result = await queue.add(async () => {
console.log(`处理文件: ${file.name}`);
// 模拟耗时操作
await new Promise(resolve => setTimeout(resolve, 1000 * Math.random()));
return { name: file.name, processed: true };
});
results.push(result);
}
return results;
}
6.4 异步状态管理
class AsyncState {
constructor(initialState = {}) {
this.state = initialState;
this.listeners = [];
this.pending = false;
}
// 获取当前状态
getState() {
return this.state;
}
// 订阅状态变化
subscribe(listener) {
this.listeners.push(listener);
// 返回取消订阅函数
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
// 异步更新状态
async update(asyncFn) {
try {
this.pending = true;
this.notifyListeners();
// 调用异步函数,传入当前状态
const newState = await asyncFn(this.state);
// 更新状态
this.state = {
...this.state,
...newState,
};
this.pending = false;
this.notifyListeners();
return this.state;
} catch (error) {
this.pending = false;
this.state = {
...this.state,
error,
};
this.notifyListeners();
throw error;
}
}
// 通知所有监听器
notifyListeners() {
for (const listener of this.listeners) {
listener({
state: this.state,
pending: this.pending
});
}
}
}
// 使用示例
const userState = new AsyncState({
user: null,
loading: false,
error: null
});
// 订阅状态变化
const unsubscribe = userState.subscribe(({ state, pending }) => {
console.log('状态更新:', state, '加载中:', pending);
});
// 异步更新状态
async function fetchUser(id) {
await userState.update(async (currentState) => {
try {
// 开始加载
const loadingState = { loading: true, error: null };
// 异步操作
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('Failed to fetch user');
const user = await response.json();
// 返回新状态
return {
user,
loading: false
};
} catch (error) {
// 返回错误状态
return {
loading: false,
error: error.message
};
}
});
}
结语
感谢您的阅读!期待您的一键三连!欢迎指正!