Lấy dữ liệu bất đồng bộ với JavaScript Fetch API
Javascript nâng cao | by
Trong thời đại web hiện đại, việc giao tiếp giữa trình duyệt và máy chủ để lấy hoặc gửi dữ liệu là điều không thể thiếu. Những thao tác như tải danh sách sản phẩm, gửi thông tin người dùng hay cập nhật giao diện theo dữ liệu server đều cần đến khả năng xử lý bất đồng bộ của JavaScript. Trước đây, các lập trình viên thường sử dụng XMLHttpRequest
, nhưng cú pháp rườm rà và khó bảo trì khiến nó dần được thay thế.
Fetch API ra đời như một giải pháp hiện đại, dễ sử dụng và mạnh mẽ hơn. Được tích hợp sẵn trong hầu hết các trình duyệt hiện nay, Fetch API cho phép chúng ta gửi và nhận dữ liệu một cách đơn giản, gọn gàng và hiệu quả, đặc biệt khi kết hợp với Promise
và async/await
. Trong bài viết này, mình sẽ cùng tìm kiếm cách sử dụng Fetch API để lấy dữ liệu bất đồng bộ, xử lý lỗi, ứng dụng thực tế, cũng như một số ví dụ minh họa để hiểu rõ hơn về công cụ hữu ích này.
Tổng quan về Fetch API trong JavaScript
Fetch API là gì?
Fetch API là một Web API hiện đại do trình duyệt cung cấp, cho phép gửi các yêu cầu HTTP hoặc HTTPS từ phía client một cách bất đồng bộ mà không cần tải lại trang. Đây là sự thay thế gọn gàng và dễ dùng hơn cho XMLHttpRequest
, vốn phức tạp và khó bảo trì.
Fetch hoạt động dựa trên cơ chế Promise, giúp viết mã rõ ràng và dễ xử lý hơn so với callback thuần. Điều này đặc biệt hữu ích khi cần xử lý chuỗi các hành động bất đồng bộ, như gọi API → lấy dữ liệu → hiển thị lên giao diện.
Cú pháp cơ bản của Fetch
fetch(url, options) .then(response => { if (!response.ok) { throw new Error("Lỗi mạng hoặc không tìm thấy dữ liệu"); } return response.json(); // hoặc .text(), .blob(), .arrayBuffer() }) .then(data => { console.log(data); // Xử lý dữ liệu thành công }) .catch(error => { console.error("Đã xảy ra lỗi:", error); // Xử lý lỗi });
Giải thích:
-
url
: đường dẫn đến API hoặc tài nguyên cần truy vấn. -
options
(tuỳ chọn): cấu hình phương thức (method
), headers, body, v.v. -
.then()
: nhận kết quả trả về nếu thành công. -
.catch()
: xử lý lỗi nếu có vấn đề trong quá trình fetch.
Ví dụ đơn giản:
fetch("https://jsonplaceholder.typicode.com/posts/1") .then(res => res.json()) .then(data => console.log("Bài viết:", data)) .catch(err => console.error("Lỗi khi lấy dữ liệu:", err));
Kết quả là dữ liệu JSON của một bài viết được in ra console mà không cần reload trang, đồng thời xử lý lỗi cũng rất rõ ràng.
Các thành phần chính trong Fetch trong JavaScript
Fetch API sử dụng cú pháp linh hoạt, có thể cấu hình nhiều tuỳ chọn đi kèm để thực hiện các hành động như GET
, POST
, PUT
, DELETE
… Việc nắm rõ các thành phần chính sẽ giúp bạn sử dụng Fetch một cách hiệu quả hơn.
URL
-
Là địa chỉ của API hoặc tài nguyên mà bạn muốn gửi yêu cầu đến.
-
Ví dụ:
fetch("https://api.example.com/users");
Options (tuỳ chọn)
Tham số thứ hai của fetch()
là một đối tượng cấu hình, nơi bạn có thể chỉ định:
method
-
Xác định loại yêu cầu HTTP bạn muốn gửi.
-
Giá trị mặc định là
'GET'
.
Ví dụ:
method: 'POST'
headers
Dùng để khai báo loại dữ liệu bạn gửi/nhận (ví dụ: JSON, plain text...).
Thường dùng với các API RESTful để gửi và nhận JSON.
Ví dụ:
headers: { "Content-Type": "application/json", "Authorization": "Bearer <token>" }
body
-
Là dữ liệu bạn muốn gửi đi – chỉ dùng với phương thức như POST, PUT.
-
Thường dùng
JSON.stringify()
để gửi object JavaScript.
Ví dụ:
body: JSON.stringify({ name: "Nguyễn Văn A", age: 30 })
Ví dụ hoàn chỉnh:
fetch("https://api.example.com/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Kiên", age: 25 }) }) .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error(err));
Đối tượng Response
Sau khi gọi fetch, kết quả trả về là một Promise chứa đối tượng Response.
Các thuộc tính và phương thức phổ biến:
-
response.ok
: boolean, trả vềtrue
nếu status nằm trong khoảng 200–299. -
response.status
: mã trạng thái HTTP (ví dụ: 200, 404, 500…). -
response.statusText
: mô tả ngắn gọn đi kèm mã trạng thái (OK, Not Found...).
Các phương thức để lấy dữ liệu thực tế:
-
response.json()
: giải mã JSON response. -
response.text()
: nhận nội dung dạng text. -
response.blob()
: dùng để xử lý ảnh, video, file nhị phân. -
response.arrayBuffer()
: dùng khi cần dữ liệu dạng buffer (âm thanh, video...).
Ví dụ:
fetch("https://api.example.com/data") .then(response => { if (response.ok) { return response.json(); } else { throw new Error(`Lỗi: ${response.status} - ${response.statusText}`); } }) .then(data => console.log("Dữ liệu:", data)) .catch(error => console.error("Đã xảy ra lỗi:", error));
Kết hợp Fetch với async/await trong JavaScript
Kể từ ES2017, JavaScript hỗ trợ cú pháp async/await
, giúp viết code bất đồng bộ trở nên dễ hiểu và gọn gàng hơn so với kiểu Promise chaining
(.then().catch()
). Khi kết hợp với Fetch API, async/await
giúp quá trình gọi API, chờ phản hồi và xử lý lỗi trở nên trực quan và dễ kiểm soát hơn.
Ưu điểm khi dùng async/await với Fetch
-
Cú pháp rõ ràng, dễ đọc như mã đồng bộ.
-
Dễ dàng sử dụng với khối
try...catch
để xử lý lỗi. -
Thích hợp cho các chuỗi xử lý phức tạp cần chờ kết quả theo thứ tự.
Cú pháp cơ bản
async function getData() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error(`HTTP Error: ${response.status}`); } const data = await response.json(); console.log(data); } catch (error) { console.error('Lỗi khi lấy dữ liệu:', error); } }
Phân tích ví dụ
-
async function getData()
: Định nghĩa một hàm bất đồng bộ. -
await fetch(...)
: Gửi yêu cầu và đợi phản hồi từ server. -
await response.json()
: Đợi dữ liệu từ response chuyển sang dạng JSON. -
try...catch
: Bắt và xử lý các lỗi trong quá trình fetch hoặc parse JSON.
Thêm ví dụ nâng cao (gửi dữ liệu POST)
async function sendData() { try { const response = await fetch('https://api.example.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Trung Kiên', age: 22 }) }); if (!response.ok) { throw new Error(`Lỗi HTTP: ${response.status}`); } const result = await response.json(); console.log('Kết quả:', result); } catch (error) { console.error('Gửi dữ liệu thất bại:', error); } }
Xử lý lỗi trong Fetch trong JavaScript
Dù Fetch API rất mạnh mẽ và hiện đại, nhưng việc xử lý lỗi đúng cách là điều bắt buộc khi làm việc với API, để tránh gây ra trải nghiệm xấu cho người dùng hoặc lỗi ngầm trong hệ thống.
Fetch không "throw" lỗi HTTP
Một điểm quan trọng cần nhớ: fetch()
chỉ throw lỗi khi có lỗi mạng (network error) hoặc khi request bị chặn, không tự động throw lỗi nếu server trả về status 4xx hay 5xx.
Ví dụ: nếu API trả về 404 Not Found
, fetch()
vẫn được coi là thành công, chỉ khi response.ok
là false
mới biết là lỗi logic từ phía server.
fetch('https://api.example.com/notfound') .then(response => { if (!response.ok) { throw new Error(`Lỗi HTTP: ${response.status}`); } return response.json(); }) .then(data => console.log(data)) .catch(error => console.error('Lỗi:', error));
Xử lý lỗi kết nối (Network Error)
Lỗi kết nối, mất mạng, domain không tồn tại... sẽ khiến fetch()
throw ra lỗi, lúc này cần bắt bằng .catch()
hoặc try...catch
(với async/await):
async function getData() { try { const response = await fetch('https://domain-khong-ton-tai.com'); if (!response.ok) { throw new Error(`Lỗi máy chủ: ${response.status}`); } const data = await response.json(); console.log(data); } catch (error) { console.error('Lỗi kết nối hoặc dữ liệu:', error.message); } }
Hiển thị lỗi thân thiện với người dùng
Không nên chỉ console.log
lỗi – hãy cung cấp phản hồi rõ ràng cho người dùng, ví dụ:
catch (error) { document.getElementById('error-message').textContent = 'Không thể tải dữ liệu. Vui lòng kiểm tra kết nối mạng hoặc thử lại sau.'; }