🎯 课程目标
完成本课程后,你将能够:
- 理解同步与异步编程的区别
- 掌握Promise的使用方法和原理
- 熟练使用async/await语法糖
- 能够处理异步操作中的错误
- 理解事件循环和宏任务/微任务
📖 异步编程概述
JavaScript是单线程语言,同步代码会阻塞程序执行。异步编程允许我们执行耗时操作(如网络请求、文件读取)时不阻塞主线程,提高程序的响应性和性能。
✨ 为什么需要异步?
- 避免阻塞:网络请求可能需要几秒,不能让页面卡死
- 提高性能:同时发起多个请求,充分利用时间
- 改善体验:用户可以继续操作,无需等待完成
- 定时任务:实现延迟执行和定时功能
⏰ 回调函数
回调是最基础的异步实现方式,将函数作为参数传递给异步操作。
// 定时器回调
console.log('开始');
setTimeout(() => {
console.log('2秒后执行');
}, 2000);
console.log('结束');
// 输出: 开始 -> 结束 -> 2秒后执行
// 数组遍历的回调
[1, 2, 3].forEach((item, index) => {
console.log(item);
});
// 事件处理回调
document.querySelector('button').addEventListener('click', (e) => {
console.log('按钮被点击');
});
// 回调地狱(Callback Hell)
// 不推荐:多层嵌套的回调难以维护
fetchData((user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
console.log(comments);
});
});
});
🤝 Promise
Promise是ES6引入的异步编程解决方案,用于表示一个异步操作的最终完成或失败。
创建Promise
// 创建Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功'); // 成功时调用
} else {
reject('操作失败'); // 失败时调用
}
}, 1000);
});
// 使用Promise
myPromise
.then(result => {
console.log(result); // '操作成功'
})
.catch(error => {
console.error(error); // '操作失败'
})
.finally(() => {
console.log('无论成功失败都会执行');
});
Promise状态
// Promise有三种状态:
// 1. pending(进行中)
// 2. fulfilled(已成功)
// 3. rejected(已失败)
const promise = new Promise((resolve, reject) => {
// 初始状态是 pending
console.log('Promise创建');
setTimeout(() => {
// 可以从pending变为fulfilled或rejected
resolve('成功');
// 或者 reject('失败');
// 注意:状态一旦改变就不会再变
}, 1000);
});
console.log('代码执行');
// 输出: Promise创建 -> 代码执行 -> 成功
Promise链式调用
// 链式调用是Promise的强大之处
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log(data);
return processData(data);
})
.then(processed => {
console.log(processed);
})
.catch(error => {
console.error('任何环节出错都会到这里');
});
// 返回Promise的then会自动被链式调用等待
function asyncOperation1() {
return new Promise(resolve => setTimeout(() => resolve(1), 1000));
}
function asyncOperation2(value) {
return new Promise(resolve => setTimeout(() => resolve(value * 2), 1000));
}
asyncOperation1()
.then(result => asyncOperation2(result))
.then(finalResult => console.log(finalResult)); // 2
Promise静态方法
// Promise.resolve() - 创建已解决的Promise
Promise.resolve('成功').then(console.log);
// Promise.reject() - 创建已拒绝的Promise
Promise.reject('失败').catch(console.error);
// Promise.all() - 等待所有Promise完成
const p1 = fetch('/api/users');
const p2 = fetch('/api/posts');
const p3 = fetch('/api/comments');
Promise.all([p1, p2, p3])
.then(([usersRes, postsRes, commentsRes]) => {
return Promise.all([
usersRes.json(),
postsRes.json(),
commentsRes.json()
]);
})
.then(([users, posts, comments]) => {
console.log(users, posts, comments);
})
.catch(error => {
console.error('任何一个失败都会触发');
});
// Promise.race() - 返回最先完成的结果
Promise.race([
fetch('/api/fast'),
new Promise(resolve => setTimeout(resolve, 5000))
]).then(response => console.log('最快的那个'));
// Promise.allSettled() - 等待所有Promise结束(无论成功失败)
Promise.allSettled([p1, p2, p3]).then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});
⚡ async/await
async/await是Promise的语法糖,让异步代码看起来像同步代码,更容易理解和维护。
基本用法
// async函数自动返回Promise
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// 调用async函数
fetchData().then(data => console.log(data));
// 也可以使用await等待结果(需要在async函数中或模块顶层)
// const data = await fetchData();
错误处理
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const user = await response.json();
return user;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error; // 可以重新抛出错误
}
}
// 或者使用catch
fetchUserData(123)
.then(user => console.log(user))
.catch(error => console.error(error));
并行执行
// 顺序执行(一个接一个)
async function sequential() {
const a = await fetch('/api/a').then(r => r.json());
const b = await fetch('/api/b').then(r => r.json());
const c = await fetch('/api/c').then(r => r.json());
return { a, b, c };
}
// 并行执行(同时发起)
async function parallel() {
const [a, b, c] = await Promise.all([
fetch('/api/a').then(r => r.json()),
fetch('/api/b').then(r => r.json()),
fetch('/api/c').then(r => r.json())
]);
return { a, b, c };
}
// 处理部分失败
async function withSomeFailures() {
const results = await Promise.allSettled([
fetch('/api/a').then(r => r.json()),
fetch('/api/b').then(r => r.json()),
fetch('/api/c').then(r => r.json())
]);
return results.map(r => r.status === 'fulfilled' ? r.value : null);
}
🔄 事件循环
理解事件循环有助于理解异步代码的执行顺序。
console.log('1'); // 同步代码,立即执行
setTimeout(() => {
console.log('2'); // 宏任务,延迟执行
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务,在同步代码后、宏任务前执行
});
requestAnimationFrame(() => {
console.log('4'); // 浏览器渲染任务
});
console.log('5');
// 执行顺序:
// 1 -> 5 -> 3 -> 4 -> 2
// 因为Promise.then是微任务,会在下一个宏任务之前执行