Men-debug JavaScript asinkron dengan Chrome DevTools

Pengantar

Fitur canggih yang membuat JavaScript unik adalah kemampuannya untuk bekerja secara asinkron melalui fungsi callback. Menetapkan callback asinkron memungkinkan Anda menulis kode berbasis peristiwa, tetapi juga membuat pelacakan bug menjadi pengalaman yang melelahkan karena JavaScript tidak dieksekusi secara linear.

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

Ringkasan teaser singkat tentang tumpukan panggilan asinkron.
Ringkasan teaser singkat tentang stack panggilan asinkron. (Kami akan segera menguraikan alur demo ini.)

Setelah mengaktifkan fitur stack panggilan asinkron di DevTools, Anda akan dapat melihat status aplikasi web Anda pada berbagai titik waktu. Ikuti pelacakan tumpukan penuh untuk beberapa pemroses peristiwa, setInterval,setTimeout, XMLHttpRequest, promise, requestAnimationFrame, MutationObservers, dan lainnya.

Saat menelusuri stack trace, Anda juga dapat menganalisis nilai variabel apa pun pada titik eksekusi runtime tertentu tersebut. 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, terdapat kotak centang baru untuk "Async". Gunakan kotak centang untuk mengaktifkan atau menonaktifkan proses debug asinkron. (Meskipun setelah diaktifkan, Anda mungkin tidak ingin menonaktifkannya.)

Aktifkan atau nonaktifkan fitur asinkron.

Merekam peristiwa timer yang tertunda dan respons XHR

Anda mungkin pernah melihatnya sebelumnya di Gmail:

Gmail mencoba mengirim email lagi.

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

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

Diagram alur contoh Gmail tiruan.
Dalam diagram di atas, metode yang ditandai dengan warna biru adalah tempat utama agar fitur DevTool baru ini menjadi yang paling bermanfaat karena metode ini berfungsi secara asinkron.

Dengan hanya melihat panel Stack Panggilan di versi DevTools 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 async diaktifkan.

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 melihat dengan mudah 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

Menonton ekspresi secara asinkron

Saat Anda menelusuri stack panggilan lengkap, ekspresi yang Anda amati juga akan diperbarui untuk mencerminkan statusnya pada saat itu.

Contoh penggunaan ekspresi watch dengan stack panggilan asinkron

Mengevaluasi kode dari cakupan sebelumnya

Selain sekadar memantau ekspresi, Anda dapat berinteraksi dengan kode dari cakupan sebelumnya langsung di panel konsol JavaScript DevTools.

Bayangkan Anda adalah Dr. Who dan Anda memerlukan sedikit bantuan untuk membandingkan jam dari sebelum Anda masuk ke Tardis hingga "sekarang". Dari konsol DevTools, Anda dapat dengan mudah mengevaluasi, menyimpan, dan melakukan penghitungan pada nilai dari berbagai titik eksekusi.

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

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

Mengurai resolusi promise berantai

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

Berikut adalah animasi kecil untuk menelusuri stack panggilan dalam contoh async-best-example.html Jake.

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

Perhatikan bahwa panel Stack Panggilan memiliki info yang cukup singkat saat mencoba men-debug promise.

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

Wow! Janji tersebut. Banyak callback.

Mendapatkan insight tentang animasi web Anda

Mari kita pelajari lebih dalam arsip HTML5Rocks. Ingat artikel Animasi yang Lebih Ringan, Lebih Efisien, dan Lebih Cepat dengan requestAnimationFrame dari Paul Lewis?

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

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

Melacak update DOM saat menggunakan MutationObserver

MutationObserver memungkinkan kita mengamati perubahan di 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 tumpukan panggilan asinkron, Anda kini dapat menelusuri tumpukan panggilan kembali melalui addNode() ke peristiwa klik awal.

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

Tips untuk men-debug JavaScript dalam tumpukan panggilan asinkron

Memberi nama fungsi

Jika cenderung menetapkan semua callback sebagai fungsi anonim, sebaiknya beri nama untuk memudahkan melihat tumpukan 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 pelacakan tumpukan DevTools dengan nama fungsinya, bukan "(anonymous function)" yang samar. Hal ini sangat memudahkan untuk melihat sekilas apa yang terjadi di stack trace Anda.

Sebelum
Fungsi anonim.
Setelah
Fungsi bernama

Mempelajari lebih lanjut

Untuk merangkum, berikut adalah semua callback asinkron tempat DevTools akan menampilkan tumpukan 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 tempat promise telah di-resolve.
  • Object.observe: Kembali ke tempat callback observer awalnya terikat.
  • MutationObservers: Kembali ke tempat peristiwa observer mutasi diaktifkan.
  • window.postMessage(): Menjalankan panggilan pesan intra-proses.
  • DataTransferItem.getAsString()
  • FileSystem API
  • 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 stack panggilan asinkron. Contoh peristiwa yang saat ini tersedia 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 untuk melihat stack trace lengkap dari callback JavaScript akan membuat Anda tidak perlu khawatir. Fitur ini di DevTools akan sangat membantu saat beberapa peristiwa asinkron terjadi dalam hubungan satu sama lain, atau jika pengecualian yang tidak tertangkap ditampilkan dari dalam callback asinkron.

Coba di Chrome. Jika Anda memiliki masukan tentang fitur baru ini, kirimkan email kepada kami di pelacak bug Chrome DevTools atau di Grup Chrome DevTools.