异步编程基础:掌握JavaScript的异步世界
本文介绍了JavaScript异步编程的核心概念,包括同步与异步的区别、事件循环机制和回调函数的使用。通过代码示例展示了同步代码的阻塞特性和异步代码的非阻塞特性,详细讲解了回调函数的基础用法、异步回调实现以及回调地狱问题。文章还演示了如何通过命名函数来减少回调嵌套,为后续学习Promise和async/await打下基础。这些内容是掌握现代JavaScript异步编程的关键知识点。
异步编程基础:掌握JavaScript的异步世界
今天我们要进入JavaScript最重要也是最具挑战性的领域——异步编程!从回调函数到Promise,再到async/await,让我们一步步征服异步编程的世界。
🎯 本周学习目标
✅ 理解同步与异步的区别和应用场景
✅ 掌握回调函数的使用和回调地狱问题
✅ 深入学习Promise的创建和使用
✅ 掌握async/await语法糖
✅ 完成API数据获取实战项目
⏰ 第一部分:同步与异步概念
1️⃣ 同步代码执行
console.log('开始执行');
function syncFunction() {
console.log('同步函数执行中...');
// 模拟耗时操作
let start = Date.now();
while (Date.now() - start < 2000) {
// 阻塞2秒
}
console.log('同步函数执行完毕');
return '同步结果';
}
console.log('调用同步函数');
let result = syncFunction();
console.log('结果:', result);
console.log('继续执行其他代码');
// 输出顺序:
// 开始执行
// 调用同步函数
// 同步函数执行中...
// (等待2秒)
// 同步函数执行完毕
// 结果: 同步结果
// 继续执行其他代码
2️⃣ 异步代码执行
console.log('开始执行');
// setTimeout - 最基本的异步函数
setTimeout(() => {
console.log('异步操作完成');
}, 2000);
console.log('继续执行其他代码');
// 输出顺序:
// 开始执行
// 继续执行其他代码
// (2秒后)
// 异步操作完成
// 更多异步API示例
console.log('1. 同步代码');
setTimeout(() => console.log('2. setTimeout 0ms'), 0);
setTimeout(() => console.log('3. setTimeout 100ms'), 100);
setImmediate(() => console.log('4. setImmediate')); // Node.js环境
Promise.resolve().then(() => console.log('5. Promise.resolve'));
console.log('6. 同步代码结束');
// 输出顺序(浏览器环境):
// 1. 同步代码
// 6. 同步代码结束
// 5. Promise.resolve
// 2. setTimeout 0ms
// 3. setTimeout 100ms
3️⃣ 事件循环机制
// 理解事件循环
console.log('=== 事件循环示例 ===');
// 宏任务 (Macro Task)
setTimeout(() => console.log('宏任务1: setTimeout'), 0);
// 微任务 (Micro Task)
Promise.resolve().then(() => console.log('微任务1: Promise'));
// 同步代码
console.log('同步代码1');
// 更多微任务
Promise.resolve().then(() => {
console.log('微任务2: Promise');
return Promise.resolve();
}).then(() => {
console.log('微任务3: Promise链');
});
// 更多宏任务
setTimeout(() => console.log('宏任务2: setTimeout'), 0);
console.log('同步代码2');
// 执行顺序:
// 同步代码1
// 同步代码2
// 微任务1: Promise
// 微任务2: Promise
// 微任务3: Promise链
// 宏任务1: setTimeout
// 宏任务2: setTimeout
📞 第二部分:回调函数
1️⃣ 回调函数基础
// 简单回调函数
function greet(name, callback) {
console.log(`Hello, ${name}!`);
callback();
}
function afterGreeting() {
console.log('Nice to meet you!');
}
greet('张三', afterGreeting);
// 匿名回调函数
greet('李四', function() {
console.log('How are you?');
});
// 箭头函数回调
greet('王五', () => {
console.log('Have a great day!');
});
// 带参数的回调
function processData(data, successCallback, errorCallback) {
if (data && data.length > 0) {
successCallback(data.toUpperCase());
} else {
errorCallback('数据为空或无效');
}
}
processData('hello',
(result) => console.log('成功:', result),
(error) => console.log('错误:', error)
);
2️⃣ 异步回调函数
// 模拟异步操作
function fetchUserData(userId, callback) {
console.log(`开始获取用户 ${userId} 的数据...`);
// 模拟网络请求延迟
setTimeout(() => {
// 模拟随机成功/失败
if (Math.random() > 0.3) {
const userData = {
id: userId,
name: `用户${userId}`,
email: `user${userId}@example.com`
};
callback(null, userData); // 第一个参数是错误,第二个是数据
} else {
callback(new Error('网络请求失败'), null);
}
}, 1000);
}
// 使用异步回调
fetchUserData(123, (error, data) => {
if (error) {
console.log('获取用户数据失败:', error.message);
} else {
console.log('获取用户数据成功:', data);
}
});
// 文件读取示例(Node.js)
const fs = require('fs'); // 仅在Node.js环境中可用
function readFileAsync(filename, callback) {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) {
callback(err, null);
} else {
callback(null, data);
}
});
}
3️⃣ 回调地狱问题
// 回调地狱示例
function getUserData(userId, callback) {
setTimeout(() => {
callback(null, { id: userId, name: `用户${userId}` });
}, 1000);
}
function getUserPosts(userId, callback) {
setTimeout(() => {
callback(null, [`帖子1`, `帖子2`]);
}, 1000);
}
function getPostComments(postId, callback) {
setTimeout(() => {
callback(null, [`评论1`, `评论2`]);
}, 1000);
}
// 嵌套回调 - 回调地狱
getUserData(1, (err, user) => {
if (err) {
console.log('错误:', err);
return;
}
console.log('用户数据:', user);
getUserPosts(user.id, (err, posts) => {
if (err) {
console.log('错误:', err);
return;
}
console.log('用户帖子:', posts);
getPostComments(posts[0], (err, comments) => {
if (err) {
console.log('错误:', err);
return;
}
console.log('帖子评论:', comments);
// 如果还有更多嵌套...这就是回调地狱!
});
});
});
// 改进:使用命名函数减少嵌套
function handleUserData(err, user) {
if (err) return console.log('错误:', err);
console.log('用户数据:', user);
getUserPosts(user.id, handleUserPosts);
}
function handleUserPosts(err, posts) {
if (err) return console.log('错误:', err);
console.log('用户帖子:', posts);
getPostComments(posts[0], handlePostComments);
}
function handlePostComments(err, comments) {
if (err) return console.log('错误:', err);
console.log('帖子评论:', comments);
}
getUserData(1, handleUserData);
🤝 第三部分:Promise详解
1️⃣ Promise基础
// 创建Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功!');
} else {
reject(new Error('操作失败!'));
}
}, 1000);
});
// 使用Promise
myPromise
.then(result => {
console.log('成功:', result);
})
.catch(error => {
console.log('失败:', error.message);
})
.finally(() => {
console.log('操作完成(无论成功失败)');
});
// Promise状态
console.log('Promise状态:', myPromise); // pending
// Promise的三种状态:
// 1. pending(待定)- 初始状态
// 2. fulfilled(已兑现)- 操作成功
// 3. rejected(已拒绝)- 操作失败
2️⃣ Promise链式调用
// 将回调地狱改写为Promise
function getUserDataPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId > 0) {
resolve({ id: userId, name: `用户${userId}` });
} else {
reject(new Error('无效的用户ID'));
}
}, 1000);
});
}
function getUserPostsPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([`用户${userId}的帖子1`, `用户${userId}的帖子2`]);
}, 1000);
});
}
function getPostCommentsPromise(postTitle) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([`${postTitle}的评论1`, `${postTitle}的评论2`]);
}, 1000);
});
}
// Promise链式调用
getUserDataPromise(1)
.then(user => {
console.log('用户数据:', user);
return getUserPostsPromise(user.id);
})
.then(posts => {
console.log('用户帖子:', posts);
return getPostCommentsPromise(posts[0]);
})
.then(comments => {
console.log('帖子评论:', comments);
})
.catch(error => {
console.log('发生错误:', error.message);
});
// 返回值的处理
Promise.resolve(42)
.then(value => {
console.log('值:', value); // 42
return value * 2;
})
.then(value => {
console.log('翻倍后:', value); // 84
return Promise.resolve(value + 10);
})
.then(value => {
console.log('最终值:', value); // 94
});
3️⃣ Promise静态方法
// Promise.resolve() - 创建已解决的Promise
const resolvedPromise = Promise.resolve('立即解决');
resolvedPromise.then(value => console.log(value));
// Promise.reject() - 创建已拒绝的Promise
const rejectedPromise = Promise.reject(new Error('立即拒绝'));
rejectedPromise.catch(error => console.log(error.message));
// Promise.all() - 等待所有Promise完成
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log('所有Promise完成:', values); // [1, 2, 3]
})
.catch(error => {
console.log('有Promise失败:', error);
});
// Promise.all() 失败示例
const fastPromise = Promise.resolve('快速完成');
const slowPromise = new Promise(resolve => setTimeout(() => resolve('慢速完成'), 2000));
const failPromise = Promise.reject(new Error('失败了'));
Promise.all([fastPromise, slowPromise, failPromise])
.then(values => {
console.log('不会执行到这里');
})
.catch(error => {
console.log('Promise.all失败:', error.message); // 立即失败
});
// Promise.allSettled() - 等待所有Promise完成(无论成功失败)
Promise.allSettled([fastPromise, slowPromise, failPromise])
.then(results => {
console.log('所有Promise都已完成:');
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} 成功:`, result.value);
} else {
console.log(`Promise ${index} 失败:`, result.reason.message);
}
});
});
// Promise.race() - 返回第一个完成的Promise
const slowResolve = new Promise(resolve => setTimeout(() => resolve('慢速成功'), 2000));
const fastResolve = new Promise(resolve => setTimeout(() => resolve('快速成功'), 1000));
Promise.race([slowResolve, fastResolve])
.then(value => {
console.log('最快完成的Promise:', value); // '快速成功'
});
// Promise.any() - 返回第一个成功的Promise(ES2021)
const failFast = Promise.reject(new Error('快速失败'));
const failSlow = Promise.reject(new Error('慢速失败'));
const succeedSlow = new Promise(resolve => setTimeout(() => resolve('慢速成功'), 2000));
Promise.any([failFast, failSlow, succeedSlow])
.then(value => {
console.log('第一个成功的Promise:', value); // '慢速成功'
})
.catch(error => {
console.log('所有Promise都失败了:', error);
});
🎭 第四部分:async/await语法
1️⃣ async/await基础
// async函数声明
async function fetchData() {
return '数据获取成功';
}
// async函数总是返回Promise
console.log(fetchData()); // Promise对象
fetchData().then(data => console.log(data)); // '数据获取成功'
// await关键字
async function getData() {
try {
const result = await fetchData();
console.log('结果:', result);
return result;
} catch (error) {
console.log('错误:', error);
}
}
getData();
// 将Promise改写为async/await
async function getUserFlow() {
try {
console.log('开始获取用户数据...');
const user = await getUserDataPromise(1);
console.log('用户数据:', user);
const posts = await getUserPostsPromise(user.id);
console.log('用户帖子:', posts);
const comments = await getPostCommentsPromise(posts[0]);
console.log('帖子评论:', comments);
return { user, posts, comments };
} catch (error) {
console.log('发生错误:', error.message);
throw error; // 重新抛出错误
}
}
getUserFlow();
2️⃣ 错误处理
// try-catch错误处理
async function handleErrors() {
try {
const result1 = await Promise.resolve('成功1');
console.log(result1);
const result2 = await Promise.reject(new Error('失败了'));
console.log('不会执行到这里');
} catch (error) {
console.log('捕获到错误:', error.message);
} finally {
console.log('清理工作');
}
}
handleErrors();
// 多个await的错误处理
async function multipleAwaits() {
try {
const user = await getUserDataPromise(1);
const posts = await getUserPostsPromise(user.id);
const comments = await getPostCommentsPromise(posts[0]);
return { user, posts, comments };
} catch (error) {
// 任何一个await失败都会被这里捕获
console.log('操作失败:', error.message);
return null;
}
}
// 单独处理每个await的错误
async function individualErrorHandling() {
let user, posts, comments;
try {
user = await getUserDataPromise(1);
} catch (error) {
console.log('获取用户数据失败:', error.message);
return;
}
try {
posts = await getUserPostsPromise(user.id);
} catch (error) {
console.log('获取帖子失败:', error.message);
posts = []; // 使用默认值
}
try {
comments = await getPostCommentsPromise(posts[0] || '默认帖子');
} catch (error) {
console.log('获取评论失败:', error.message);
comments = [];
}
return { user, posts, comments };
}
3️⃣ 并发处理
// 串行执行(一个接一个)
async function serialExecution() {
console.time('串行执行');
const result1 = await new Promise(resolve => setTimeout(() => resolve('结果1'), 1000));
const result2 = await new Promise(resolve => setTimeout(() => resolve('结果2'), 1000));
const result3 = await new Promise(resolve => setTimeout(() => resolve('结果3'), 1000));
console.timeEnd('串行执行'); // 约3秒
return [result1, result2, result3];
}
// 并行执行(同时开始)
async function parallelExecution() {
console.time('并行执行');
const promise1 = new Promise(resolve => setTimeout(() => resolve('结果1'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('结果2'), 1000));
const promise3 = new Promise(resolve => setTimeout(() => resolve('结果3'), 1000));
const results = await Promise.all([promise1, promise2, promise3]);
console.timeEnd('并行执行'); // 约1秒
return results;
}
// 使用Promise.all与await结合
async function efficientDataFetching() {
try {
// 同时发起多个请求
const [userData, userPosts, userFriends] = await Promise.all([
getUserDataPromise(1),
getUserPostsPromise(1),
Promise.resolve(['朋友1', '朋友2']) // 模拟获取朋友列表
]);
console.log('用户数据:', userData);
console.log('用户帖子:', userPosts);
console.log('用户朋友:', userFriends);
return { userData, userPosts, userFriends };
} catch (error) {
console.log('获取数据失败:', error.message);
}
}
// 限制并发数量
async function limitedConcurrency(urls, limit = 3) {
const results = [];
for (let i = 0; i < urls.length; i += limit) {
const batch = urls.slice(i, i + limit);
const batchPromises = batch.map(url => fetch(url));
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
🌐 第五部分:实战项目 - API数据获取
让我们创建一个完整的API数据获取和展示系统:
// API数据获取类
class APIClient {
constructor(baseURL) {
this.baseURL = baseURL;
this.defaultHeaders = {
'Content-Type': 'application/json'
};
}
// 基础请求方法
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
headers: { ...this.defaultHeaders, ...options.headers },
...options
};
try {
console.log(`发起请求: ${config.method || 'GET'} ${url}`);
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log('请求成功:', data);
return data;
} catch (error) {
console.error('请求失败:', error.message);
throw error;
}
}
// GET请求
async get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url);
}
// POST请求
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
// PUT请求
async put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
// DELETE请求
async delete(endpoint) {
return this.request(endpoint, {
method: 'DELETE'
});
}
}
// 用户数据管理类
class UserManager {
constructor() {
this.api = new APIClient('https://jsonplaceholder.typicode.com');
this.cache = new Map();
this.loadingStates = new Map();
}
// 获取用户列表
async getUsers() {
const cacheKey = 'users';
// 检查缓存
if (this.cache.has(cacheKey)) {
console.log('从缓存获取用户列表');
return this.cache.get(cacheKey);
}
// 检查是否正在加载
if (this.loadingStates.get(cacheKey)) {
console.log('用户列表正在加载中...');
return this.loadingStates.get(cacheKey);
}
// 开始加载
const loadingPromise = this.api.get('/users');
this.loadingStates.set(cacheKey, loadingPromise);
try {
const users = await loadingPromise;
this.cache.set(cacheKey, users);
return users;
} finally {
this.loadingStates.delete(cacheKey);
}
}
// 获取单个用户
async getUser(userId) {
const cacheKey = `user_${userId}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const user = await this.api.get(`/users/${userId}`);
this.cache.set(cacheKey, user);
return user;
} catch (error) {
if (error.message.includes('404')) {
throw new Error(`用户 ${userId} 不存在`);
}
throw error;
}
}
// 获取用户帖子
async getUserPosts(userId) {
const cacheKey = `posts_${userId}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
const posts = await this.api.get('/posts', { userId });
this.cache.set(cacheKey, posts);
return posts;
}
// 获取用户完整信息(用户+帖子)
async getUserProfile(userId) {
try {
// 并行获取用户信息和帖子
const [user, posts] = await Promise.all([
this.getUser(userId),
this.getUserPosts(userId)
]);
return {
...user,
posts,
postCount: posts.length
};
} catch (error) {
console.error(`获取用户 ${userId} 的完整信息失败:`, error.message);
throw error;
}
}
// 搜索用户
async searchUsers(query) {
const users = await this.getUsers();
const lowerQuery = query.toLowerCase();
return users.filter(user =>
user.name.toLowerCase().includes(lowerQuery) ||
user.email.toLowerCase().includes(lowerQuery) ||
user.username.toLowerCase().includes(lowerQuery)
);
}
// 批量获取用户信息
async getBatchUsers(userIds, batchSize = 3) {
const results = [];
for (let i = 0; i < userIds.length; i += batchSize) {
const batch = userIds.slice(i, i + batchSize);
console.log(`处理批次 ${Math.floor(i / batchSize) + 1}: 用户 ${batch.join(', ')}`);
const batchPromises = batch.map(id =>
this.getUser(id).catch(error => ({ error: error.message, id }))
);
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
// 避免请求过于频繁
if (i + batchSize < userIds.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
// 清除缓存
clearCache() {
this.cache.clear();
console.log('缓存已清除');
}
}
// 使用示例
async function demonstrateAsyncProgramming() {
const userManager = new UserManager();
try {
console.log('=== 异步编程演示 ===\n');
// 1. 获取用户列表
console.log('1. 获取用户列表');
const users = await userManager.getUsers();
console.log(`获取到 ${users.length} 个用户\n`);
// 2. 获取特定用户信息
console.log('2. 获取用户详细信息');
const userProfile = await userManager.getUserProfile(1);
console.log(`用户 ${userProfile.name} 有 ${userProfile.postCount} 篇帖子\n`);
// 3. 搜索用户
console.log('3. 搜索用户');
const searchResults = await userManager.searchUsers('Bret');
console.log(`搜索结果: ${searchResults.length} 个用户\n`);
// 4. 批量获取用户
console.log('4. 批量获取用户信息');
const batchResults = await userManager.getBatchUsers([1, 2, 3, 999]);
const successCount = batchResults.filter(result => !result.error).length;
console.log(`批量获取完成: ${successCount} 个成功\n`);
// 5. 演示缓存效果
console.log('5. 演示缓存效果');
console.time('第二次获取用户列表');
await userManager.getUsers();
console.timeEnd('第二次获取用户列表');
} catch (error) {
console.error('演示过程中发生错误:', error.message);
}
}
// 运行演示
demonstrateAsyncProgramming();
// 错误处理和重试机制
class RetryableAPIClient extends APIClient {
async requestWithRetry(endpoint, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await this.request(endpoint, options);
} catch (error) {
lastError = error;
console.log(`请求失败 (尝试 ${attempt}/${maxRetries}):`, error.message);
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 指数退避
console.log(`等待 ${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`请求失败,已重试 ${maxRetries} 次: ${lastError.message}`);
}
}
💡 本周总结
🎯 核心知识点
- 异步概念:同步vs异步、事件循环机制
- 回调函数:基础用法和回调地狱问题
- Promise:创建、链式调用、静态方法
- async/await:语法糖、错误处理、并发控制
- 实际应用:API请求、数据处理、缓存机制
📝 最佳实践
✅ 优先使用async/await而不是Promise.then
✅ 合理使用Promise.all进行并发处理
✅ 总是处理异步操作的错误
✅ 避免在循环中使用await(除非需要串行)
✅ 使用缓存减少重复请求
🤔 思考题
- 什么时候使用Promise.all,什么时候使用Promise.allSettled?
- 如何实现请求的超时和重试机制?
- async/await相比Promise有什么优势和劣势?
📚 下周预告
下周我们将学习 ES6+新特性,掌握现代JavaScript的强大功能!
内容包括:
- 箭头函数和模板字符串
- let/const和块级作用域
- 类和继承
- 模块化编程
🎉 写在最后
异步编程是JavaScript的精髓,掌握它让你能够构建高性能的现代Web应用!
如果这篇文章对你有帮助:
👍 请点个赞,让更多人看到
🔄 转发分享,帮助更多学习异步编程的朋友
💬 评论区留言,分享你的异步编程经验
关于作者
每周更新实用的前端教程,从入门到进阶
关注我,一起在前端的道路上成长!
#JavaScript #异步编程 #Promise #AsyncAwait #API请求
更多推荐



所有评论(0)