Men-debug JavaScript asinkron dengan Chrome DevTools

Pengantar

Fitur canggih yang membuat JavaScript unik adalah kemampuannya untuk bekerja secara asinkron melalui fungsi callback. Dengan menetapkan callback asinkron, Anda dapat menulis kode berdasarkan peristiwa, tetapi juga menjadikan pelacakan bug menjadi pengalaman menarik karena JavaScript tidak dieksekusi secara linier.

Untungnya, kini di Chrome DevTools, Anda dapat melihat stack panggilan lengkap dari callback JavaScript asinkron.

Ringkasan singkat tentang stack panggilan asinkron.
Ringkasan singkat tentang stack panggilan asinkron. (Kami akan menjelaskan alur demo ini segera.)

Setelah mengaktifkan fitur async call stack di DevTools, Anda akan dapat melihat status aplikasi web pada berbagai waktu. Telusuri stack trace lengkap untuk beberapa pemroses peristiwa, setInterval,setTimeout, XMLHttpRequest, promise, requestAnimationFrame, MutationObservers, dan lainnya.

Saat menjalankan pelacakan tumpukan, Anda juga dapat menganalisis nilai variabel apa pun pada titik eksekusi runtime tertentu. Ini seperti mesin waktu untuk ekspresi smartwatch Anda!

Mari kita aktifkan fitur ini dan lihat beberapa skenario berikut.

Mengaktifkan proses debug asinkron di Chrome

Coba fitur baru ini dengan mengaktifkannya di Chrome. Buka panel Sources di Chrome Canary DevTools.

Di samping panel Call Stack di sisi kanan, ada kotak centang baru untuk "Async". Gunakan kotak centang untuk mengaktifkan atau menonaktifkan proses debug asinkron. (Meskipun sudah aktif, Anda mungkin tidak ingin menonaktifkannya.)

Aktifkan atau nonaktifkan fitur asinkron.

Mengambil peristiwa timer yang tertunda dan respons XHR

Anda mungkin pernah melihat ini sebelumnya di Gmail:

Gmail mencoba mengirim email.

Jika ada masalah saat mengirim permintaan (baik server mengalami masalah atau ada masalah konektivitas jaringan di sisi klien), Gmail akan otomatis mencoba mengirim ulang pesan setelah waktu tunggu singkat habis.

Untuk melihat bagaimana stack panggilan asinkron dapat membantu kita menganalisis peristiwa timer yang tertunda dan respons XHR, saya telah membuat ulang alur tersebut dengan contoh Gmail tiruan. Kode JavaScript lengkap dapat ditemukan di link di atas, tetapi alurnya adalah sebagai berikut:

Diagram alir contoh Gmail tiruan.
Dalam diagram di atas, metode yang disorot dengan warna biru adalah titik utama untuk fitur DevTool baru ini menjadi yang paling bermanfaat karena metode ini bekerja secara asinkron.

Dengan hanya melihat panel Call Stack di DevTools versi sebelumnya, titik henti sementara dalam postOnFail() akan memberi Anda sedikit informasi tentang tempat postOnFail() dipanggil. Namun, lihat perbedaannya saat mengaktifkan stack asinkron:

Sebelum
Titik henti sementara ditetapkan dalam contoh Gmail tiruan tanpa stack panggilan asinkron.
Panel Call Stack tanpa pengaktifan asinkron.

Di sini Anda dapat melihat bahwa postOnFail() dimulai dari callback AJAX, tetapi tidak ada info lebih lanjut.

Setelah
Titik henti sementara ditetapkan dalam contoh Gmail tiruan dengan stack panggilan asinkron.
Panel Call Stack dengan asinkron diaktifkan.

Di sini Anda dapat melihat bahwa XHR dimulai dari submitHandler(). Bagus!

Dengan mengaktifkan stack panggilan asinkron, Anda dapat melihat seluruh stack panggilan untuk dengan mudah melihat apakah permintaan dimulai dari submitHandler() (yang terjadi setelah mengklik tombol kirim) atau dari retrySubmit() (yang terjadi setelah penundaan setTimeout()):

submitHandler()
Titik henti sementara ditetapkan dalam contoh Gmail tiruan dengan stack panggilan asinkron
retrySubmit()
Titik henti sementara lain yang ditetapkan dalam contoh Gmail tiruan dengan stack panggilan asinkron

Ekspresi smartwatch secara asinkron

Saat Anda menjalankan stack panggilan lengkap, ekspresi yang dipantau juga akan diperbarui untuk mencerminkan status saat itu juga.

Contoh penggunaan ekspresi watch dengan stack panggilan aysnc

Mengevaluasi kode dari cakupan sebelumnya

Selain hanya mengamati ekspresi, Anda bisa berinteraksi dengan kode dari cakupan sebelumnya langsung di panel konsol JavaScript DevTools.

Bayangkan Anda adalah Dr. Who dan Anda perlu sedikit bantuan untuk membandingkan jam dari sebelum Anda masuk ke Tardis dengan "sekarang". Dari konsol DevTools, Anda bisa dengan mudah mengevaluasi, menyimpan, dan melakukan perhitungan nilai dari berbagai titik eksekusi yang berbeda.

Contoh penggunaan konsol JavaScript dengan stack panggilan aysnc.
Gunakan konsol JavaScript bersama stack panggilan asinkron untuk men-debug kode Anda. Demo di atas dapat ditemukan di sini.

Tetap di dalam DevTools untuk memanipulasi ekspresi akan menghemat waktu Anda karena tidak harus beralih kembali ke kode sumber, mengedit, dan memuat ulang browser.

Mengurai resolusi promise yang dirantai

Jika Anda merasa alur Gmail tiruan sebelumnya akan sulit diuraikan tanpa mengaktifkan fitur stack panggilan asinkron, dapatkah Anda membayangkan betapa sulitnya dengan alur asinkron yang lebih kompleks seperti promise yang dirantai? Mari kita lihat kembali contoh terakhir tutorial Jake Archibald tentang JavaScript Promise.

Berikut ini adalah sedikit animasi menjalankan stack panggilan di contoh async-best-example.html Jake.

Sebelum
Titik henti sementara ditetapkan dalam contoh promise tanpa stack panggilan asinkron
Panel Call Stack tanpa pengaktifan asinkron.

Perhatikan bahwa panel Call Stack cukup pendek tentang info saat mencoba men-debug promise.

Setelah
Breakpoint disetel dalam contoh promise dengan stack panggilan asinkron.
Panel Call Stack dengan asinkron diaktifkan.

Wah! Janji itu. Banyak callback.

Dapatkan insight tentang animasi web Anda

Mari kita masuk lebih dalam ke arsip HTML5Rocks. Ingat karya Paul Lewis Leaner, Meaner, quick Animations with requestAnimationFrame?

Buka demo requestAnimationFrame dan tambahkan titik henti sementara di awal metode update() (sekitar baris 874) post.html. Dengan stack panggilan asinkron, kita mendapatkan lebih banyak insight tentang requestAnimationFrame, termasuk kemampuan untuk kembali ke callback peristiwa scroll yang dimulai.

Sebelum
Titik henti sementara ditetapkan dalam contoh requestAnimationFrame tanpa stack panggilan asinkron.
Panel Call Stack tanpa pengaktifan asinkron.
Setelah
Titik henti sementara ditetapkan dalam contoh requestAnimationFrame dengan stack panggilan asinkron
Dan dengan asinkron diaktifkan.

Melacak pembaruan DOM saat menggunakan MutationObserver

MutationObserver memungkinkan kita untuk mengamati perubahan dalam DOM. Dalam contoh sederhana ini, saat Anda mengklik tombol, node DOM baru akan ditambahkan ke <div class="rows"></div>.

Tambahkan titik henti sementara dalam nodeAdded() (baris 31) di demo.html. Dengan mengaktifkan stack panggilan asinkron, Anda kini dapat menjalankan stack panggilan kembali melalui addNode() ke peristiwa klik awal.

Sebelum
Titik henti sementara ditetapkan dalam contoh mutationObserver tanpa stack panggilan asinkron.
Panel Call Stack tanpa pengaktifan asinkron.
Setelah
Breakpoint disetel dalam contoh mutationObserver dengan stack panggilan asinkron.
Dan dengan asinkron diaktifkan.

Tips untuk men-debug JavaScript dalam stack panggilan asinkron

Beri nama fungsi Anda

Jika Anda cenderung menetapkan semua callback sebagai fungsi anonim, sebaiknya beri nama callback untuk memudahkan tampilan stack panggilan.

Misalnya, ambil fungsi anonim seperti ini:

window.addEventListener('load', function() {
  // do something
});

Lalu, beri nama seperti windowLoaded():

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

Saat diaktifkan, peristiwa pemuatan akan muncul di stack trace DevTools dengan nama fungsinya, bukan "(anonymous function)" samar. Hal ini sangat memudahkan Anda untuk melihat sekilas apa yang terjadi dalam pelacakan tumpukan.

Sebelum
Fungsi anonim.
Setelah
Fungsi bernama

Jelajahi lebih lanjut

Singkatnya, ini adalah semua callback asinkron tempat DevTools akan menampilkan stack panggilan lengkap:

  • Timer: Kembali ke tempat setTimeout() atau setInterval() diinisialisasi.
  • XHR: Kembali ke tempat xhr.send() dipanggil.
  • Frame animasi: Kembali ke tempat requestAnimationFrame dipanggil.
  • Promise: Kembali ke promise yang telah diselesaikan.
  • Object.observe: Kembali ke tempat callback observer awalnya terikat.
  • MutationObservers: Kembali ke tempat peristiwa pengamat mutasi diaktifkan.
  • window.postMessage(): Berjalanlah melalui panggilan pesan intra-proses.
  • DataTransferItem.getAsString()
  • API FileSystem
  • IndexedDB
  • WebSQL
  • Peristiwa DOM yang memenuhi syarat melalui addEventListener(): Kembali ke tempat peristiwa diaktifkan. Karena alasan performa, tidak semua peristiwa DOM memenuhi syarat untuk fitur async call stack. Contoh peristiwa yang tersedia saat ini meliputi: 'scroll', 'hashchange', dan 'selectionchange'.
  • Peristiwa multimedia melalui addEventListener(): Kembali ke tempat peristiwa diaktifkan. Peristiwa multimedia yang tersedia mencakup: peristiwa audio dan video (misalnya 'play', 'pause', 'ratechange'), peristiwa WebRTC MediaStreamTrackList (misalnya, 'addtrack', 'removetrack'), dan peristiwa MediaSource (misalnya 'sourceopen').

Kemampuan melihat stack trace lengkap callback JavaScript akan membuat Anda tetap tenang. Fitur ini di DevTools akan sangat membantu saat beberapa peristiwa asinkron terjadi dalam kaitannya satu sama lain, atau jika pengecualian yang tidak tertangkap dilempar dari dalam callback asinkron.

Cobalah di Chrome. Jika Anda memiliki masukan tentang fitur baru ini, sampaikan kepada kami di pelacak bug Chrome DevTools atau di Grup Chrome DevTools.