Giới thiệu
Một tính năng mạnh mẽ khiến JavaScript trở nên độc đáo là khả năng hoạt động không đồng bộ thông qua các hàm gọi lại. Việc chỉ định lệnh gọi lại không đồng bộ cho phép bạn viết mã do sự kiện điều khiển, nhưng cũng khiến việc theo dõi lỗi trở nên khó khăn vì JavaScript không thực thi theo cách tuyến tính.
May mắn thay, giờ đây, trong Công cụ dành cho nhà phát triển của Chrome, bạn có thể xem ngăn xếp lệnh gọi đầy đủ của các lệnh gọi lại JavaScript không đồng bộ!
Sau khi bật tính năng ngăn xếp lệnh gọi không đồng bộ trong Công cụ cho nhà phát triển, bạn sẽ có thể đi sâu vào trạng thái của ứng dụng web tại nhiều thời điểm. Đi theo dấu vết ngăn xếp đầy đủ cho một số trình nghe sự kiện, setInterval
,setTimeout
, XMLHttpRequest
, lời hứa, requestAnimationFrame
, MutationObservers
, v.v.
Khi đi theo dấu vết ngăn xếp, bạn cũng có thể phân tích giá trị của bất kỳ biến nào tại thời điểm cụ thể đó của quá trình thực thi thời gian chạy. Đây giống như một cỗ máy thời gian cho các biểu thức đồng hồ!
Hãy bật tính năng này và xem một số trường hợp sau.
Bật tính năng gỡ lỗi không đồng bộ trong Chrome
Hãy dùng thử tính năng mới này bằng cách bật tính năng này trong Chrome. Chuyển đến bảng điều khiển Sources (Nguồn) của Chrome Canary DevTools.
Bên cạnh bảng điều khiển Call Stack (Ngăn xếp lệnh gọi) ở bên phải, có một hộp đánh dấu mới cho "Async" (Không đồng bộ). Bật hoặc tắt tính năng gỡ lỗi không đồng bộ bằng cách bật/tắt hộp đánh dấu. (Mặc dù sau khi bật, bạn có thể không bao giờ muốn tắt tính năng này.)
Ghi lại các sự kiện bộ hẹn giờ bị trễ và phản hồi XHR
Có thể bạn đã thấy thông báo này trong Gmail:
Nếu xảy ra sự cố khi gửi yêu cầu (máy chủ gặp sự cố hoặc có vấn đề về kết nối mạng ở phía máy khách), Gmail sẽ tự động thử gửi lại thư sau một khoảng thời gian chờ ngắn.
Để xem cách ngăn xếp lệnh gọi không đồng bộ có thể giúp chúng ta phân tích các sự kiện hẹn giờ bị trì hoãn và phản hồi XHR, tôi đã tạo lại luồng đó bằng một ví dụ mô phỏng Gmail. Bạn có thể tìm thấy mã JavaScript đầy đủ trong đường liên kết ở trên, nhưng quy trình sẽ như sau:
Khi chỉ xem bảng điều khiển ngăn xếp lệnh gọi trong các phiên bản DevTools trước, một điểm ngắt trong postOnFail()
sẽ cung cấp cho bạn ít thông tin về vị trí gọi postOnFail()
. Nhưng hãy xem sự khác biệt khi bật ngăn xếp không đồng bộ:
Khi bật ngăn xếp lệnh gọi không đồng bộ, bạn có thể xem toàn bộ ngăn xếp lệnh gọi để dễ dàng xem liệu yêu cầu có được bắt đầu từ submitHandler()
(xảy ra sau khi nhấp vào nút gửi) hay từ retrySubmit()
(xảy ra sau khi trễ setTimeout()
):
Xem biểu thức không đồng bộ
Khi bạn đi qua ngăn xếp lệnh gọi đầy đủ, biểu thức được theo dõi cũng sẽ cập nhật để phản ánh trạng thái của biểu thức đó tại thời điểm đó!
Đánh giá mã từ các phạm vi trước
Ngoài việc chỉ xem biểu thức, bạn có thể tương tác với mã của mình từ các phạm vi trước đó ngay trong bảng điều khiển JavaScript của DevTools.
Hãy tưởng tượng bạn là Tiến sĩ Who và bạn cần một chút trợ giúp để so sánh đồng hồ từ trước khi bạn lên Tardis cho đến "bây giờ". Trên bảng điều khiển DevTools, bạn có thể dễ dàng đánh giá, lưu trữ và tính toán các giá trị từ nhiều điểm thực thi.
Việc ở lại trong DevTools để thao tác với biểu thức sẽ giúp bạn tiết kiệm thời gian không phải chuyển về mã nguồn, chỉnh sửa và làm mới trình duyệt.
Giải quyết các giải pháp hứa hẹn theo chuỗi
Nếu bạn cho rằng luồng Gmail mô phỏng trước đó khó được giải mã nếu không bật tính năng ngăn xếp lệnh gọi không đồng bộ, thì bạn có thể tưởng tượng được việc giải mã sẽ khó khăn hơn bao nhiêu với các luồng không đồng bộ phức tạp hơn như các lời hứa theo chuỗi? Hãy cùng xem lại ví dụ cuối cùng trong hướng dẫn của Jake Archibald về Lời hứa JavaScript.
Dưới đây là một ảnh động nhỏ về cách đi qua ngăn xếp lệnh gọi trong ví dụ async-best-example.html của Jake.
Nhận thông tin chi tiết về ảnh động trên web
Hãy cùng tìm hiểu sâu hơn về kho lưu trữ HTML5Rocks. Bạn còn nhớ bài viết Ảnh động gọn nhẹ, mạnh mẽ và nhanh hơn với requestAnimationFrame của Paul Lewis không?
Mở bản minh hoạ requestAnimationFrame và thêm một điểm ngắt ở đầu phương thức update() (khoảng dòng 874) của post.html. Với ngăn xếp lệnh gọi không đồng bộ, chúng ta có được nhiều thông tin chi tiết hơn về requestAnimationFrame, bao gồm cả khả năng quay lại lệnh gọi lại sự kiện cuộn ban đầu.
Theo dõi các bản cập nhật DOM khi sử dụng MutationObserver
MutationObserver
cho phép chúng ta quan sát các thay đổi trong DOM. Trong ví dụ đơn giản này, khi bạn nhấp vào nút, một nút DOM mới sẽ được thêm vào <div class="rows"></div>
.
Thêm điểm ngắt trong nodeAdded()
(dòng 31) trong demo.html. Khi bật ngăn xếp lệnh gọi không đồng bộ, giờ đây, bạn có thể quay lại ngăn xếp lệnh gọi thông qua addNode()
đến sự kiện nhấp ban đầu.
Mẹo gỡ lỗi JavaScript trong ngăn xếp lệnh gọi không đồng bộ
Đặt tên cho hàm
Nếu thường gán tất cả lệnh gọi lại dưới dạng hàm ẩn danh, bạn nên đặt tên cho các lệnh gọi lại đó để dễ dàng xem ngăn xếp lệnh gọi hơn.
Ví dụ: lấy một hàm ẩn danh như sau:
window.addEventListener('load', function() {
// do something
});
Và đặt tên cho lớp này như windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
Khi sự kiện tải kích hoạt, sự kiện này sẽ xuất hiện trong dấu vết ngăn xếp của DevTools với tên hàm thay vì "(hàm ẩn danh)" khó hiểu. Nhờ đó, bạn có thể dễ dàng xem nhanh những gì đang diễn ra trong dấu vết ngăn xếp.
Khám phá thêm
Tóm lại, đây là tất cả các lệnh gọi lại không đồng bộ mà DevTools sẽ hiển thị ngăn xếp lệnh gọi đầy đủ:
- Bộ hẹn giờ: Quay lại vị trí khởi chạy
setTimeout()
hoặcsetInterval()
. - XHR: Quay lại vị trí gọi
xhr.send()
. - Khung ảnh động: Quay lại vị trí gọi
requestAnimationFrame
. - Lời hứa: Quay lại nơi lời hứa đã được giải quyết.
- Object.observe: Quay lại vị trí ban đầu liên kết lệnh gọi lại của trình quan sát.
- MutationObservers: Quay lại vị trí đã kích hoạt sự kiện trình quan sát đột biến.
- window.postMessage(): Xem qua các lệnh gọi nhắn tin trong quy trình.
- DataTransferItem.getAsString()
- FileSystem API
- IndexedDB
- WebSQL
- Sự kiện DOM đủ điều kiện thông qua
addEventListener()
: Quay lại vị trí sự kiện được kích hoạt. Do lý do về hiệu suất, không phải sự kiện DOM nào cũng đủ điều kiện sử dụng tính năng ngăn xếp lệnh gọi không đồng bộ. Ví dụ về các sự kiện hiện có: "scroll", "hashchange" và "selectionchange". - Sự kiện đa phương tiện thông qua
addEventListener()
: Quay lại vị trí sự kiện được kích hoạt. Các sự kiện đa phương tiện hiện có bao gồm: sự kiện âm thanh và video (ví dụ: "play", "pause", "ratechange"), sự kiện WebRTC MediaStreamTrackList (ví dụ: "addtrack", "removetrack") và sự kiện MediaSource (ví dụ: "sourceopen").
Việc có thể xem toàn bộ dấu vết ngăn xếp của lệnh gọi lại JavaScript sẽ giúp bạn giải quyết được vấn đề này. Tính năng này trong DevTools sẽ đặc biệt hữu ích khi nhiều sự kiện không đồng bộ xảy ra liên quan đến nhau hoặc nếu một ngoại lệ chưa phát hiện được được gửi từ trong lệnh gọi lại không đồng bộ.
Hãy dùng thử trong Chrome. Nếu bạn muốn phản hồi về tính năng mới này, hãy gửi thư cho chúng tôi trên công cụ theo dõi lỗi của Công cụ của Chrome cho nhà phát triển hoặc trong Nhóm Công cụ của Chrome cho nhà phát triển.