个人主页: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')]);// 并行解析 JSONconst [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) {// 创建处理每项的 Promiseconst promise = processItem(item).then(result => {// 从运行列表中移除完成的 Promiserunning.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 };}});
}
结语
感谢您的阅读!期待您的一键三连!欢迎指正!