JavaScript 是单线程的,异步编程允许程序在等待某些操作完成的同时继续执行其他代码,如网络请求、文件读写等。本章将介绍 JavaScript 中的异步编程和网络请求。
回调函数是异步编程的最基本形式,它作为参数传递给另一个函数,并在特定事件发生时被调用。
// 基本回调函数function fetchData(callback) {setTimeout(() => {const data = { id: 1, name: '张三' };callback(data);}, 1000);}fetchData(data => {console.log('获取到数据:', data);});// 错误处理回调function fetchDataWithError(successCallback, errorCallback) {setTimeout(() => {const random = Math.random();if (random > 0.5) {const data = { id: 1, name: '张三' };successCallback(data);} else {errorCallback(new Error('获取数据失败'));}}, 1000);}fetchDataWithError(data => console.log('获取到数据:', data),error => console.error('错误:', error.message));
当有多个异步操作需要按顺序执行时,使用回调函数会导致代码嵌套过深,形成"回调地狱"。
// 回调地狱示例function step1(callback) {setTimeout(() => {console.log('步骤1完成');callback();}, 1000);}function step2(callback) {setTimeout(() => {console.log('步骤2完成');callback();}, 1000);}function step3(callback) {setTimeout(() => {console.log('步骤3完成');callback();}, 1000);}// 嵌套回调step1(() => {step2(() => {step3(() => {console.log('所有步骤完成');});});});
Promise 是处理异步操作的一种更现代的方式,它提供了更清晰的语法和更好的错误处理机制。
// 创建 Promiseconst promise = new Promise((resolve, reject) => {// 异步操作setTimeout(() => {const random = Math.random();if (random > 0.5) {resolve({ id: 1, name: '张三' });} else {reject(new Error('获取数据失败'));}}, 1000);});// 使用 Promisepromise.then(data => {console.log('获取到数据:', data);return data.id; // 返回一个值,传递给下一个 then}).then(id => {console.log('用户ID:', id);}).catch(error => {console.error('错误:', error.message);}).finally(() => {console.log('无论成功或失败都会执行');});
Promise 可以通过链式调用处理多个异步操作。
function fetchUser(id) {return new Promise((resolve, reject) => {setTimeout(() => {resolve({ id, name: '张三' });}, 1000);});}function fetchUserPosts(userId) {return new Promise((resolve, reject) => {setTimeout(() => {resolve([{ id: 1, title: '文章1', userId },{ id: 2, title: '文章2', userId }]);}, 1000);});}function fetchPostComments(postId) {return new Promise((resolve, reject) => {setTimeout(() => {resolve([{ id: 1, text: '评论1', postId },{ id: 2, text: '评论2', postId }]);}, 1000);});}// Promise 链fetchUser(1).then(user => {console.log('获取到用户:', user);return fetchUserPosts(user.id);}).then(posts => {console.log('获取到文章:', posts);return fetchPostComments(posts[0].id);}).then(comments => {console.log('获取到评论:', comments);}).catch(error => {console.error('错误:', error.message);});
Promise 提供了一些静态方法来处理多个 Promise。
// Promise.all - 等待所有 Promise 完成const promise1 = Promise.resolve(1);const promise2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));const promise3 = new Promise(resolve => setTimeout(() => resolve(3), 2000));Promise.all([promise1, promise2, promise3]).then(values => {console.log('所有 Promise 完成:', values); // [1, 2, 3]}).catch(error => {console.error('错误:', error.message);});// Promise.race - 等待第一个 Promise 完成Promise.race([promise1, promise2, promise3]).then(value => {console.log('第一个完成的 Promise:', value); // 1}).catch(error => {console.error('错误:', error.message);});// Promise.allSettled - 等待所有 Promise 完成,无论成功或失败const promise4 = Promise.reject(new Error('失败'));Promise.allSettled([promise1, promise2, promise3, promise4]).then(results => {console.log('所有 Promise 已解决:', results);// [// { status: 'fulfilled', value: 1 },// { status: 'fulfilled', value: 2 },// { status: 'fulfilled', value: 3 },// { status: 'rejected', reason: Error: 失败 }// ]});
async/await 是基于 Promise 的语法糖,它使异步代码看起来更像同步代码,更易于理解和维护。
// 定义异步函数async function fetchData() {try {const response = await new Promise((resolve, reject) => {setTimeout(() => {const random = Math.random();if (random > 0.5) {resolve({ id: 1, name: '张三' });} else {reject(new Error('获取数据失败'));}}, 1000);});console.log('获取到数据:', response);return response;} catch (error) {console.error('错误:', error.message);throw error; // 重新抛出错误}}// 调用异步函数fetchData().then(data => {console.log('处理数据:', data);}).catch(error => {console.error('处理错误:', error.message);});
可以使用 Promise.all 和 async/await 并行执行多个异步操作。
async function fetchUserAndPosts(userId) {try {// 并行执行两个异步操作const [user, posts] = await Promise.all([fetchUser(userId),fetchUserPosts(userId)]);console.log('用户:', user);console.log('文章:', posts);return { user, posts };} catch (error) {console.error('错误:', error.message);throw error;}}fetchUserAndPosts(1).then(result => {console.log('获取成功:', result);}).catch(error => {console.error('获取失败:', error.message);});
async/await 使串行执行多个异步操作变得简单。
async function processUserData(userId) {try {// 串行执行异步操作const user = await fetchUser(userId);console.log('获取到用户:', user);const posts = await fetchUserPosts(user.id);console.log('获取到文章:', posts);const comments = await fetchPostComments(posts[0].id);console.log('获取到评论:', comments);return { user, posts, comments };} catch (error) {console.error('处理用户数据时出错:', error.message);throw error;}}processUserData(1).then(result => {console.log('处理成功:', result);}).catch(error => {console.error('处理失败:', error.message);});
Fetch API 是一种现代的、基于 Promise 的网络请求接口,用于在浏览器中发送 HTTP 请求。
// 发送 GET 请求fetch('https://api.example.com/users').then(response => {if (!response.ok) {throw new Error(`HTTP 错误! 状态: ${response.status}`);}return response.json();}).then(data => {console.log('获取到数据:', data);}).catch(error => {console.error('请求失败:', error.message);});// 使用 async/awaitasync function fetchUsers() {try {const response = await fetch('https://api.example.com/users');if (!response.ok) {throw new Error(`HTTP 错误! 状态: ${response.status}`);}const data = await response.json();console.log('获取到数据:', data);return data;} catch (error) {console.error('请求失败:', error.message);throw error;}}
// 发送 POST 请求const userData = {name: '张三',email: 'zhangsan@example.com'};fetch('https://api.example.com/users', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(userData)}).then(response => {if (!response.ok) {throw new Error(`HTTP 错误! 状态: ${response.status}`);}return response.json();}).then(data => {console.log('创建成功:', data);}).catch(error => {console.error('创建失败:', error.message);});// 使用 async/awaitasync function createUser(userData) {try {const response = await fetch('https://api.example.com/users', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(userData)});if (!response.ok) {throw new Error(`HTTP 错误! 状态: ${response.status}`);}const data = await response.json();console.log('创建成功:', data);return data;} catch (error) {console.error('创建失败:', error.message);throw error;}}
实现一个 retry
函数,它接受一个返回 Promise 的函数和最大重试次数,当 Promise 失败时自动重试。
创建一个函数,模拟从服务器获取用户数据,使用 Promise 实现。函数应该有一个参数表示是否模拟请求失败,并在控制台输出结果。
创建三个函数,分别模拟获取用户信息、用户文章和文章评论。然后实现两种方式获取数据:串行(一个接一个)和并行(同时请求)。