Bạn có thể dùng JavaScript để kiểm tra xem ứng dụng của mình có kết nối Internet không?
Trong bài viết này, tôi sẽ đưa ra câu trả lời cập nhật cho câu hỏi phát hiện kết nối Internet này. (Thở dài! Nói nhanh 5 lần cho vui!)
Giải pháp sẽ dùng Fetch API của JavaScript và mã bất đồng bộ với Async & Await. Nhưng trước, hãy xem một giải pháp được chấp nhận rộng rãi và thảo luận tại sao nó có thể không phải lựa chọn tốt nhất cho ứng dụng của bạn.
navigator.onLine
Thuộc tính online của giao diện navigator, navigator.onLine, thường được dùng để phát hiện trạng thái trực tuyến và ngoại tuyến của trình duyệt.
Kết hợp với trình lắng nghe sự kiện online và offline, nó dường như cung cấp một giải pháp đơn giản cho nhà phát triển và dễ triển khai.
Hãy xem cách triển khai navigator.onLine
Bắt đầu bằng cách thêm trình lắng nghe sự kiện load. Khi sự kiện load kích hoạt, trình lắng nghe sẽ kiểm tra thuộc tính online của giao diện navigator và hiển thị trạng thái trực tuyến.
Thuộc tính online của navigator cung cấp phản hồi boolean (true hoặc false). Để hoàn thành hành động của trình lắng nghe, chúng ta sẽ dùng câu lệnh ternary để đặt giá trị hiển thị trạng thái.window.addEventListener("load", (event) => { const statusDisplay = document.getElementById("status"); statusDisplay.textContent = navigator.onLine ? "Online" : "OFFline"; });Tại sao lại là navigator? Vâng, đó là tham chiếu đến trình duyệt Netscape Navigator những năm 90.
Căn giữa phần tử h1 trong trang HTML với id là "status". Nếu áp dụng mã JavaScript trên vào trang, bạn sẽ thấy nó hiển thị "Online".
Nhưng đây chỉ cập nhật phần tử h1 khi trang tải. Hãy thêm trình lắng nghe sự kiện offline và online để cập nhật hiển thị trạng thái bất cứ khi nào một trong hai sự kiện đó kích hoạt.
window.addEventListener("offline", (event) => { const statusDisplay = document.getElementById("status"); statusDisplay.textContent = "OFFline"; }); window.addEventListener("online", (event) => { const statusDisplay = document.getElementById("status"); statusDisplay.textContent = "Online"; });Chúng ta có thể vào tab Application của Chrome Dev Tools và nhấp vào ServiceWorker để đặt trình duyệt phản hồi như thể nó ngoại tuyến.
Chọn và bỏ chọn hộp kiểm Offline vài lần. Bạn sẽ thấy hiển thị trạng thái phản hồi ngay lập tức với các sự kiện offline và online được kích hoạt.

Hãy đào sâu một chút
Àn ấn đầu tiên, giải pháp trên có vẻ tốt và khá đơn giản. Rất tiếc, khi đọc thêm về thuộc tính online của navigator và các sự kiện online/offline, chúng ta phát hiện có vấn đề.
Tìm kiếm navigator.onLine trên CanIUse.com cho thấy hỗ trợ rộng rãi cho trạng thái online/offline mà thuộc tính cung cấp. Tuy nhiên, nhìn vào ghi chú dưới bảng hỗ trợ, chúng ta thấy
“Online không luôn có nghĩa là kết nối Internet. Nó cũng có thể chỉ có nghĩa là kết nối với một số mạng”.
Hmm, điều đó gây rắc rối một chút.
Vì vậy nếu bạn thực sự muốn xác định trạng thái trực tuyến của trình duyệt, bạn nên phát triển thêm phương pháp kiểm tra.
Hãy cũng xem tài liệu tham khảo MDN cho navigator.onLine. MDN web docs xác nhận thông tin CanIUse.com và thêm ghi chú.
“Các trình duyệt triển khai thuộc tính này khác nhau... bạn không thể giả định giá trị true có nghĩa là trình duyệt có thể truy cập Internet. Bạn có thể nhận được false positives (dương tính giả)...”
Và điều đó xác nhận nỗi lo của chúng ta về việc dùng thuộc tính online của navigator làm giải pháp phát hiện kết nối Internet. Đây là giải pháp có thể gây rối loạn trong ứng dụng phụ thuộc vào việc biết khi nào nguồn dữ liệu bên ngoài có sẵn.
Ví dụ như khi chúng ta thử xác định xem một Progressive Web App có trực tuyến hay không. MDN thậm chí khuyến nghị,
“...nếu bạn thực sự muốn xác định trạng thái trực tuyến của trình duyệt, bạn nên phát triển thêm phương pháp kiểm tra.”
Tìm kiếm nhanh trên web cho “navigator online not working”كشف nhiều bài đăng diễn đàn nơi những người phụ thuộc vào thuộc tính này gặp vấn đề.
Vậy giải pháp là gì?
Chúng ta cần biết khi nào ứng dụng thực sự kết nối Internet, không chỉ router hay mạng cục bộ. Hãy quay lại tệp JavaScript và bắt đầu lại.
Ý tưởng là tạo một yêu cầu và xử lý nhẹ nhàng với bắt lỗi nếu thất bại. Nếu yêu cầu thành công, chúng ta trực tuyến, nếu thất bại, chúng ta không.
Chúng ta sẽ yêu cầu một hình ảnh nhỏ (1 pixel) theo khoảng thời gian để xác định trạng thái trực tuyến. JavaScript hiện đại cung cấp Fetch API và mã bất đồng bộ với Async & Await. Chúng ta sẽ dùng các công cụ này để đạt mục tiêu.
checkOnlineStatus()
Hãy bắt đầu bằng cách tạo một hàm mũi tên async tên checkOnlineStatus. Hàm sẽ trả về true hoặc false như thuộc tính online của navigator.
Bên trong hàm, chúng ta thiết lập khối try nơi chúng ta await một yêu cầu fetch cho hình ảnh 1 pixel. Đảm bảo service worker của bạn không cache hình ảnh này.
Mã phản hồi HTTP từ 200 đến 299 chỉ thị thành công, và chúng ta sẽ trả về kết quả so sánh mã trạng thái. Điều này sẽ là true nếu mã trạng thái phản hồi từ 200 đến 299 và false ngược lại.
Chúng ta cũng phải cung cấp khối catch bắt lỗi nếu yêu cầu thất bại. Chúng ta sẽ trả về false trong khối catch để chỉ ra chúng ta chắc chắn ngoại tuyến nếu điều đó xảy ra.
const checkOnlineStatus = async () => { try { const online = await fetch("/1pixel.png"); return online.status >= 200 && online.status < 300; // true hoặc false } catch (err) { return false; // chắc chắn offline } };Tiếp theo, chúng ta sẽ dùng phương thức setInterval và truyền cho nó một hàm ẩn danh async. Hàm async sẽ await kết quả của hàm checkOnlineStatus. Sau đó dùng câu lệnh ternary với kết quả để hiển thị trạng thái trực tuyến hiện tại.
Để kiểm tra ví dụ này, đặt độ trễ interval là 3 giây (3000 mili giây). Điều này thực sự quá thường xuyên. Kiểm tra mỗi 30 giây (30000 mili giây) có thể đủ cho nhu cầu thực tế của bạn.
setInterval(async () => { const result = await checkOnlineStatus(); const statusDisplay = document.getElementById("status"); statusDisplay.textContent = result ? "Online" : "OFFline"; }, 3000); // có thể quá thường xuyên, thử 30000 cho mỗi 30 giâyVới mã mới đã lưu, hãy quay lại tab Application trong Chrome Dev Tools để kiểm tra phản hồi ngoại tuyến.

差点忘了 bao gồm trình lắng nghe sự kiện load với chức năng async! Phát hiện sự kiện load có lẽ chỉ quan trọng nếu bạn có Progressive Web App dùng service worker cho khả năng ngoại tuyến. Nếu không, trang web hoặc ứng dụng của bạn đơn giản sẽ không tải nếu không có kết nối.
Đây là trình lắng nghe sự kiện load mới:
window.addEventListener("load", async (event) => { const statusDisplay = document.getElementById("status"); statusDisplay.textContent = (await checkOnlineStatus()) ? "Online" : "OFFline"; });Một suy nghĩ cuối cùng
Mã interval ở trên tốt cho việc hiển thị trạng thái kết nối trong ứng dụng. Tuy nhiên, tôi không khuyên nên dựa vào trạng thái kết nối được kiểm tra 20 hoặc 30 giây trước khi thực hiện yêu cầu dữ liệu quan trọng trong ứng dụng.
Do đó, bạn nên gọi hàm checkOnlineStatus trực tiếp trước yêu cầu và đánh giá phản hồi trước khi yêu cầu dữ liệu.
const yourDataRequestFunction = async () => { const online = await checkOnlineStatus(); if (online) { // thực hiện yêu cầu dữ liệu } }Kết luận
Mặc dù navigator.onLine được hỗ trợ rộng rãi, nó cung cấp kết quả không tin cậy khi xác định xem ứng dụng có thực sự kết nối Internet hay không. Sử dụng Fetch API và JavaScript bất đồng bộ, chúng ta có thể nhanh chóng viết một giải pháp tin cậy hơn.
Đây là link đến code gist trên GitHub, và đây là video hướng dẫn tôi đã làm: