Làm việc với các tác vụ bất đồng bộ là một phần không thể thiếu trong phát triển ứng dụng hiện đại, đặc biệt là khi xây dựng ứng dụng React. Nếu bạn từng cảm thấy "chóng mặt" khi đối mặt với các chuỗi lệnh .then() lồng nhau (callback hell), thì async/await chính là giải pháp giúp bạn xử lý bất đồng bộ một cách dễ hiểu và gọn gàng hơn.
Hãy đồng hành cùng mình đi qua bài viết này, để hiểu rõ hơn về async/await nhé.
1. Async/Await là gì?
Async/await là một cú pháp của JavaScript để xử lý các tác vụ bất đồng bộ. Nó hoạt động dựa trên Promise.
- async: khi đặt trước một hàm, key này sẽ biến hàm đó thành một hàm bất đồng bộ. Nó sẽ luôn trả về một Promise.
- await: được dùng bên trong hàm async để "chờ" Promise hoàn thành trước khi tiếp tục thực thi các dòng code tiếp theo.
Bạn có thể hiểu một cách đơn giản như này:
- async/await giúp code bất đồng bộ của bạn trông giống như mã đồng bộ.
- Thay vì xử lý .then() hoặc .catch(), bạn dùng try...catch để bắt lỗi.
Mình sẽ lấy một ví dụ đơn giản như lấy danh sách người dùng, để có cái nhìn tổng quan hơn nha
- Với Promise và .then():
fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => {
console.log("Dữ liệu nhận được:", data);
})
.catch((error) => {
console.error("Lỗi:", error);
});
- Với async/await:
const fetchData = async () => {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log("Dữ liệu nhận được:", data);
} catch (error) {
console.error("Lỗi:", error);
}
};
fetchData();
Như bạn thấy, với async/await, đoạn code trở nên dễ đọc hơn nhiều vì nó không bị phân mảnh bởi chuỗi .then(). Bạn có thể viết code bất đồng bộ trông giống như code đồng bộ, giúp việc theo dõi luồng dữ liệu và xử lý lỗi trở nên dễ dàng hơn.
2. Tại sao nên sử dụng Async/await
2.1 Ưu điểm
- async/await giúp bạn viết code trông gọn gàng hơn, dễ theo dõi luồng thực thi.
- Sử dụng try...catch giúp bạn bắt và xử lý lỗi ở một nơi, thay vì phải thêm .catch() sau mỗi Promise.
- Có thể hoạt động trong React, Node.js và nhiều thư viện khác.
2.2 Khi nào nên sử dụng Async/await
- Khi call API để lấy dữ liệu trong React.
- Khi cần thực hiện các tác vụ bất đồng bộ như truy xuất cơ sở dữ liệu, xử lý file, hoặc chờ một hiệu ứng đặc biệt hoàn thành.
- Khi bạn muốn thay thế các đoạn code Promise .then() phức tạp bằng một cú pháp dễ đọc hơn.
3. Hướng dẫn sử dụng Async/await trong React
Bây giờ, hãy cùng nhau tìm hiểu cách áp dụng async/await trong một dự án React thông qua các ví dụ.
3.1 Call API get user list
Mình sẽ tạo một component UserList để hiển thị danh sách người dùng từ API https://jsonplaceholder.typicode.com/users
.
import React, { useState, useEffect } from "react";
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error("Lỗi mạng, không thể tải dữ liệu");
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsers();
// Cleanup function nếu cần
return () => {
};
}, []);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Lỗi: {error}</p>;
}
return (
<div>
<h1>Danh sách người dùng</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
export default UserList;
- useState: dùng để quản lý dữ liệu người dùng, trạng thái loading và error.
- useEffect: dùng để thực hiện side-effect, ở đây là call API khi component được render lần đầu tiên.
- async/await trong fetchUsers: call API và xử lý dữ liệu tuần tự và dễ hiểu.
- try...catch: bắt và xử lý error khi call API.
- Kiểm tra
response.ok
: đảm bảo phản hồi từ server là thành công trước khi xử lý dữ liệu. - Hiển thị trạng thái: tùy thuộc vào loading và error, component sẽ hiển thị thông báo tương ứng hoặc danh sách người dùng.
3.2 Call nhiều API liên tiếp
Giả sử bạn cần gọi hai API liên tiếp: một để lấy danh sách người dùng, một để lấy danh sách bài viết. Bạn có thể sử dụng await để đảm bảo các lệnh call API được thực hiện tuần tự.
import React, { useState, useEffect } from "react";
const PostsAndUsers = () => {
const [users, setUsers] = useState([]);
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const userResponse = await fetch("https://jsonplaceholder.typicode.com/users");
if (!userResponse.ok) {
throw new Error("Lỗi khi tải danh sách người dùng");
}
const userData = await userResponse.json();
setUsers(userData);
const postResponse = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!postResponse.ok) {
throw new Error("Lỗi khi tải danh sách bài viết");
}
const postData = await postResponse.json();
setPosts(postData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Lỗi: {error}</p>;
return (
<div>
<h1>Người dùng và Bài viết</h1>
<h2>Người dùng:</h2>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<h2>Bài viết:</h2>
<ul>
{posts.slice(0, 10).map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
export default PostsAndUsers;
- Đoạn code trên gọi hai API tuần tự, đảm bảo dữ liệu người dùng được tải trước bài viết.
- Sử dụng await giúp bạn chờ kết quả của từng API call trước khi tiếp tục.
- Quản lý lỗi và loading tương tự như ở ví dụ trước.
3.3 Tích hợp với Axios
Axios là một thư viện HTTP Client phổ biến giúp bạn call API dễ dàng hơn. Axios hỗ trợ Promise, do đó bạn hoàn toàn có thể sử dụng async/await.
Trước tiên, để sử dụng nó bạn cần cài đặt Axios:
pnpm install axios
import React, { useState, useEffect } from "react";
import axios from "axios";
const UserListWithAxios = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get("https://jsonplaceholder.typicode.com/users");
setUsers(response.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Lỗi: {error}</p>;
return (
<div>
<h1>Danh sách người dùng (Axios)</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
export default UserListWithAxios;
- Thay thế
fetch
bằngaxios.get
, cú pháp tương tự nhưng Axios tự động chuyển đổi dữ liệu JSON và cung cấp thêm nhiều tính năng hữu ích. - Axios có thể cung cấp thông tin error chi tiết, giúp bạn dễ debug hơn.
4. Lưu ý khi sử dụng Async/await trong React
- Cá nhân mình thấy không nên gọi async trực tiếp trong useEffect, thay vào đó nên bọc trong một function con. Mặc dù bạn có thể khai báo useEffect là một hàm async, nhưng điều này không được khuyến khích vì useEffect - hàm trả về void hoặc một hàm cleanup. Thay vào đó, hãy bọc code async của bạn trong một hàm con bên trong useEffect.
useEffect(() => {
const fetchData = async () => {
// Code async ở đây
};
fetchData();
}, []);
- Nếu component của bạn bị unmount trước khi tác vụ async hoàn thành, việc cập nhật state có thể gây ra lỗi. Để tránh điều này, bạn có thể sử dụng một biến cờ (isMounted) để kiểm tra.
useEffect(() => {
let isMounted = true;
const fetchData = async () => {
try {
const data = await fetchDataFromAPI();
if (isMounted) {
setData(data);
}
} catch (error) {
}
};
fetchData();
return () => {
isMounted = false;
};
}, []);
- Đừng bỏ qua try...catch khi sử dụng async/await. Các tác vụ bất đồng bộ có thể thất bại, và việc bắt lỗi sẽ giúp ứng dụng nice hơn.
- Khi call API bất đồng bộ, việc hiển thị loading cho người dùng là rất quan trọng, để cải thiện trải nghiệm người dùng.
5. Kết luận
Async/await mang lại cách tiếp cận đơn giản và hiệu quả hơn để xử lý bất đồng bộ trong JavaScript, đặc biệt là khi bạn làm việc với React. Từ các API call đơn giản đến việc quản lý nhiều đoạn code call API liên tiếp, async/await giúp mã của bạn dễ đọc và bảo trì hơn rất nhiều.
Hy vọng bài viết này giúp bạn hiểu rõ và áp dụng async/await hiệu quả trong dự án React của mình.
Các bài viết liên quan:
Bài viết liên quan
Vercel là gì? Hướng dẫn deploy dự án Next.js bằng Vercel
Dec 07, 2024 • 14 min read
So sánh giữa HOCs, Render Props và Hooks.
Dec 05, 2024 • 8 min read
Render Props pattern là gì? Hướng dẫn sử dụng Render Props
Dec 03, 2024 • 8 min read
HOCs Pattern là gì? Hướng dẫn triển khai Hocs Pattern trong dự án React
Dec 02, 2024 • 7 min read
Hooks Pattern là gì? Hướng dẫn áp dụng Hooks Pattern trong dự án React
Nov 28, 2024 • 11 min read
Promise là gì? Hướng dẫn sử dụng Promise trong dự án React
Nov 27, 2024 • 7 min read