JavaScript 异步编程学习笔记
js 是单线程,但可以通过异步实现非阻塞。比如网络请求、文件读取这些耗时操作不会阻塞其他代码执行。
回调函数
最早的异步处理方式。
javascript
function fetchData(callback) {
setTimeout(() => {
callback({ name: "js" });
}, 1000);
}
fetchData((data) => {
console.log(data);
});回调地狱:
javascript
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
// 嵌套太深了
});
});
});DANGER
回调函数嵌套过深会导致"回调地狱",代码难以阅读和维护。
Promise
Promise 是 ES6 引入的异步编程解决方案,解决了回调地狱的问题。
基本用法
javascript
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("操作成功");
} else {
reject("操作失败");
}
}, 1000);
});
promise
.then((result) => {
console.log(result); // "操作成功"
})
.catch((error) => {
console.error(error); // "操作失败"
});Promise 链式调用
javascript
fetchUserData()
.then((user) => {
return fetchUserPosts(user.id);
})
.then((posts) => {
return fetchPostComments(posts[0].id);
})
.then((comments) => {
console.log(comments);
})
.catch((error) => {
console.error("发生错误:", error);
});Promise 静态方法
Promise.all()
javascript
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // [1, 2, 3]
})
.catch((error) => {
console.error(error);
});TIP
Promise.all() 会等待所有 Promise 都成功,如果有一个失败,整个 Promise.all() 就会失败。
Promise.race()
javascript
const promise1 = new Promise((resolve) => setTimeout(() => resolve(1), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 500));
Promise.race([promise1, promise2])
.then((value) => {
console.log(value); // 2 (最先完成的)
});Promise.allSettled()
javascript
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("错误");
Promise.allSettled([promise1, promise2])
.then((results) => {
console.log(results);
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: '错误' }
// ]
});async/await
async/await 是 ES2017 引入的语法糖,让异步代码看起来像同步代码。
基本用法
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error("获取数据失败:", error);
}
}
fetchData();并行执行多个异步操作
javascript
async function fetchMultipleData() {
try {
// 并行执行
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
console.log(user, posts, comments);
} catch (error) {
console.error(error);
}
}async/await 与 Promise 结合
javascript
async function processData() {
const user = await fetchUser();
const posts = await fetchUserPosts(user.id);
const comments = await fetchPostComments(posts[0].id);
return { user, posts, comments };
}
processData()
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});实际应用示例
使用 fetch API
javascript
async function getUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const userData = await response.json();
return userData;
} catch (error) {
console.error("获取用户数据失败:", error);
throw error;
}
}错误处理最佳实践
javascript
async function safeAsyncOperation() {
try {
const result = await riskyOperation();
return { success: true, data: result };
} catch (error) {
// 记录错误但不中断程序
console.error("操作失败:", error);
return { success: false, error: error.message };
}
}总结
- 回调函数:最基础的异步处理方式,但容易产生回调地狱
- Promise:解决了回调地狱问题,提供了更好的错误处理
- async/await:让异步代码更易读,是推荐的现代写法