README ini berisi informasi yang telah saya pelajari selama bertahun-tahun tentang menangani kesalahan JavaScript, melaporkannya ke server, dan menavigasi banyak bug yang dapat membuat semuanya menjadi sangat sulit. Browser telah mengalami peningkatan dalam hal ini, namun masih ada ruang yang tersisa untuk ditingkatkan untuk memastikan bahwa semua aplikasi dapat menangani kesalahan apa pun yang terjadi dengan baik dan bijaksana.
Kasus pengujian untuk konten yang ditemukan dalam panduan ini dapat ditemukan di https://mknichel.github.io/javascript-errors/.
Daftar isi
Perkenalan
Anatomi Kesalahan JavaScript
Menghasilkan Kesalahan JavaScript
Pesan Kesalahan
Format Pelacakan Tumpukan
Menangkap Kesalahan JavaScript
window.onerror
coba/tangkap
Titik Masuk yang Dilindungi
Janji
Pekerja Web
Ekstensi Chrome
Menangkap, melaporkan, dan memperbaiki kesalahan adalah bagian penting dari aplikasi apa pun untuk memastikan kesehatan dan stabilitas aplikasi. Karena kode JavaScript juga dijalankan pada klien dan di banyak lingkungan browser yang berbeda, mungkin sulit untuk selalu mengetahui Kesalahan JS dari aplikasi Anda. Tidak ada spesifikasi web formal tentang cara melaporkan kesalahan JS yang menyebabkan perbedaan dalam implementasi setiap browser. Selain itu, ada banyak bug dalam penerapan kesalahan JavaScript di browser yang membuat hal ini semakin sulit. Halaman ini menavigasi melalui aspek-aspek Kesalahan JS sehingga pengembang di masa depan dapat menangani kesalahan dengan lebih baik dan diharapkan browser akan menyatu pada solusi standar.
Kesalahan JavaScript terdiri dari dua bagian utama: pesan kesalahan dan pelacakan tumpukan . Pesan kesalahan adalah string yang menjelaskan apa yang salah, dan pelacakan tumpukan menjelaskan di mana kesalahan terjadi dalam kode. Kesalahan JS dapat dihasilkan oleh browser itu sendiri atau disebabkan oleh kode aplikasi.
Kesalahan JS dapat terjadi oleh browser ketika sepotong kode tidak dijalankan dengan benar, atau dapat terjadi langsung oleh kode.
Misalnya:
var a = 3;a();
Dalam contoh ini, variabel yang sebenarnya berupa angka tidak dapat dipanggil sebagai fungsi. Browser akan memunculkan kesalahan seperti TypeError: a is not a function
with a stack trace yang menunjuk ke baris kode tersebut.
Pengembang mungkin juga ingin membuat kesalahan pada sepotong kode jika prasyarat tertentu tidak terpenuhi. Misalnya
jika (!checkPrecondition()) { throw new Error("Tidak memenuhi prasyarat!");}
Dalam hal ini, kesalahannya adalah Error: Doesn't meet precondition!
. Kesalahan ini juga akan berisi jejak tumpukan yang menunjuk ke baris yang sesuai. Kesalahan yang ditimbulkan oleh browser dan kode aplikasi dapat ditangani dengan cara yang sama.
Ada beberapa cara pengembang dapat membuat kesalahan dalam JavaScript:
throw new Error('Problem description.')
throw Error('Problem description.')
<-- setara dengan yang pertama
throw 'Problem description.'
<-- buruk
throw null
<-- bahkan lebih buruk lagi
Melempar string atau null sangat tidak disarankan karena browser tidak akan melampirkan jejak tumpukan ke kesalahan tersebut, sehingga kehilangan konteks di mana kesalahan tersebut terjadi dalam kode. Yang terbaik adalah membuang objek Error yang sebenarnya, yang akan berisi pesan kesalahan serta jejak tumpukan yang menunjuk ke baris kode yang tepat di mana kesalahan terjadi.
Setiap browser memiliki kumpulan pesannya sendiri yang digunakan untuk pengecualian bawaan, seperti contoh di atas untuk mencoba memanggil non-fungsi. Browser akan mencoba menggunakan pesan yang sama, tetapi karena tidak ada spesifikasinya, hal ini tidak dijamin. Misalnya, Chrome dan Firefox menggunakan {0} is not a function
untuk contoh di atas sementara IE11 akan melaporkan Function expected
(terutama juga tanpa melaporkan variabel apa yang coba dipanggil).
Namun, browser juga cenderung sering berbeda. Ketika ada beberapa pernyataan default dalam pernyataan switch
, Chrome akan menampilkan "More than one default clause in switch statement"
sementara Firefox akan melaporkan "more than one switch default"
. Saat fitur baru ditambahkan ke web, pesan kesalahan ini harus diperbarui. Perbedaan ini dapat terjadi nanti ketika Anda mencoba menangani kesalahan yang dilaporkan dari kode yang dikaburkan.
Anda dapat menemukan template yang digunakan browser untuk pesan kesalahan di:
Firefox - http://mxr.mozilla.org/mozilla1.9.1/source/js/src/js.msg
Chrome - https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/messages.js
Internet Explorer - https://github.com/Microsoft/ChakraCore/blob/4e4d4f00f11b2ded23d1885e85fc26fcc96555da/lib/Parser/rterrors.h
Browser akan menghasilkan pesan kesalahan yang berbeda untuk beberapa pengecualian.
Pelacakan tumpukan adalah deskripsi tempat terjadinya kesalahan dalam kode. Ini terdiri dari serangkaian frame, di mana setiap frame menggambarkan baris tertentu dalam kode. Bingkai paling atas adalah lokasi di mana kesalahan terjadi, sedangkan bingkai berikutnya adalah tumpukan panggilan fungsi - atau bagaimana kode dieksekusi untuk sampai ke titik di mana kesalahan terjadi. Karena JavaScript biasanya digabungkan dan diperkecil, penting juga untuk memiliki nomor kolom sehingga pernyataan yang tepat dapat ditemukan ketika suatu baris memiliki banyak pernyataan.
Pelacakan tumpukan dasar di Chrome terlihat seperti:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Setiap bingkai tumpukan terdiri dari nama fungsi (jika berlaku dan kode tidak dieksekusi dalam lingkup global), skrip asalnya, dan nomor baris dan kolom kode.
Sayangnya, tidak ada standar untuk format pelacakan tumpukan sehingga hal ini berbeda tergantung browser.
Pelacakan tumpukan Microsoft Edge dan IE 11 terlihat mirip dengan Chrome kecuali secara eksplisit mencantumkan kode Global:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:3) at Global code (http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3)
Jejak tumpukan Firefox terlihat seperti:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9 @http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Format Safari mirip dengan format Firefox tetapi juga sedikit berbeda:
throwError@http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:18 global code@http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:13
Informasi dasarnya sama, tetapi formatnya berbeda.
Perhatikan juga bahwa dalam contoh Safari, selain formatnya berbeda dari Chrome, nomor kolomnya juga berbeda dari Chrome dan Firefox. Nomor kolom juga dapat menyimpang lebih banyak dalam situasi kesalahan yang berbeda - misalnya dalam kode (function namedFunction() { throwError(); })();
, Chrome akan melaporkan kolom untuk pemanggilan fungsi throwError()
sementara IE11 melaporkan nomor kolom sebagai awal string. Perbedaan ini akan kembali berlaku nanti ketika server perlu mengurai jejak tumpukan untuk kesalahan yang dilaporkan dan membatalkan penyamaran jejak tumpukan yang dikaburkan.
Lihat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack untuk informasi lebih lanjut tentang properti tumpukan kesalahan. Saat mengakses properti Error.stack, Chrome menyertakan pesan kesalahan sebagai bagian dari tumpukan tetapi Safari 10+ tidak.
Format pelacakan tumpukan berbeda menurut browser dalam bentuk dan nomor kolom yang digunakan.
Lebih dalam lagi, ada banyak perbedaan dalam format pelacakan tumpukan yang dibahas di bagian di bawah ini.
Secara default, fungsi anonim tidak memiliki nama dan muncul sebagai string kosong atau "Fungsi anonim" dalam nama fungsi di pelacakan tumpukan (tergantung pada browser). Untuk meningkatkan proses debug, Anda harus menambahkan nama ke semua fungsi untuk memastikan nama tersebut muncul di bingkai tumpukan. Cara termudah untuk melakukannya adalah dengan memastikan bahwa fungsi anonim ditentukan dengan nama, meskipun nama tersebut tidak digunakan di tempat lain. Misalnya:
setTimeout(fungsi namaOfTheAnonymousFunction() { ... }, 0);
Ini akan menyebabkan jejak tumpukan berpindah dari:
at http://mknichel.github.io/javascript-errors/javascript-errors.js:125:17
ke
at nameOfTheAnonymousFunction (http://mknichel.github.io/javascript-errors/javascript-errors.js:121:31)
Di Safari, ini dimulai dari:
https://mknichel.github.io/javascript-errors/javascript-errors.js:175:27
ke
nameOfTheAnonymousFunction@https://mknichel.github.io/javascript-errors/javascript-errors.js:171:41
Metode ini memastikan bahwa nameOfTheAnonymousFunction
muncul dalam bingkai untuk kode apa pun dari dalam fungsi tersebut, membuat proses debug menjadi lebih mudah. Lihat http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/#toc-debugging-tips untuk informasi lebih lanjut.
Browser juga akan menggunakan nama variabel atau properti yang ditugaskan ke suatu fungsi jika fungsi itu sendiri tidak memiliki nama. Misalnya, di
var fnVariableName = fungsi() { ... };
browser akan menggunakan fnVariableName
sebagai nama fungsi dalam pelacakan tumpukan.
at throwError (http://mknichel.github.io/javascript-errors/javascript-errors.js:27:9) at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Bahkan lebih bernuansa dari itu, jika variabel ini didefinisikan dalam fungsi lain, semua browser hanya akan menggunakan nama variabel sebagai nama fungsi dalam pelacakan tumpukan kecuali untuk Firefox, yang akan menggunakan bentuk berbeda yang menggabungkan nama fungsi luar dengan nama variabel dalam. Contoh:
fungsi throwErrorFromInnerFunctionAssignedToVariable() { var fnVariableName = function() { melempar Kesalahan baru("foo"); }; fnNamaVariabel();}
akan menghasilkan di Firefox:
throwErrorFromInnerFunctionAssignedToVariable/fnVariableName@http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37
Di browser lain, tampilannya akan seperti ini:
at fnVariableName (http://mknichel.github.io/javascript-errors/javascript-errors.js:169:37)
Firefox menggunakan teks bingkai tumpukan berbeda untuk fungsi yang ditentukan dalam fungsi lain.
Nama tampilan suatu fungsi juga dapat diatur oleh properti displayName
di semua browser utama kecuali IE11. Di browser ini, displayName akan muncul di debugger devtools, namun di semua browser kecuali Safari, displayName tidak akan digunakan di Jejak tumpukan kesalahan (Safari berbeda dari yang lain karena juga menggunakan displayName di pelacakan tumpukan yang terkait dengan kesalahan).
var someFunction = function() {};someFunction.displayName = " # Deskripsi fungsi yang lebih panjang.";
Tidak ada spesifikasi resmi untuk properti displayName, namun didukung oleh semua browser utama. Lihat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName dan http://www.alertdebugging.com/2009/04/29/building-a-better -javascript-profiler-with-webkit/ untuk informasi lebih lanjut tentang nama tampilan.
IE11 tidak mendukung properti displayName.
Safari menggunakan properti displayName sebagai nama simbol dalam Pelacakan tumpukan kesalahan.
Jika kesalahan dilaporkan tanpa jejak tumpukan (lihat detail lebih lanjut kapan hal ini akan terjadi di bawah), maka pelacakan tumpukan dapat ditangkap secara terprogram.
Di Chrome, ini sangat mudah dilakukan dengan menggunakan Error.captureStackTrace
API. Lihat https://github.com/v8/v8/wiki/Stack%20Trace%20API untuk informasi lebih lanjut tentang penggunaan API ini.
Misalnya:
fungsi abaikanFunctionInStackTrace() { var err = Kesalahan baru(); Error.captureStackTrace(err, abaikanThisFunctionInStackTrace); kembalikan err.stack;}
Di browser lain, pelacakan tumpukan juga dapat dikumpulkan dengan membuat kesalahan baru dan mengakses properti tumpukan objek tersebut:
var err = Kesalahan baru('');return err.stack;
Namun, IE10 hanya mengisi pelacakan tumpukan ketika kesalahan benar-benar terjadi:
mencoba { melempar Kesalahan baru('');} tangkapan (e) { kembalikan e.stack;}
Jika tidak satu pun dari pendekatan ini berhasil, maka dimungkinkan untuk membuat pelacakan tumpukan kasar tanpa nomor baris atau kolom dengan melakukan iterasi pada objek arguments.callee.caller
- ini tidak akan berfungsi dalam Mode Ketat ES5 dan ini bukan pendekatan yang disarankan.
Sangat umum titik asinkron dimasukkan ke dalam kode JavaScript, seperti saat kode menggunakan setTimeout
atau melalui penggunaan Promises. Titik masuk asinkron ini dapat menyebabkan masalah pada pelacakan tumpukan, karena menyebabkan terbentuknya konteks eksekusi baru dan pelacakan tumpukan dimulai dari awal lagi.
Chrome DevTools memiliki dukungan untuk pelacakan tumpukan asinkron, atau dengan kata lain memastikan pelacakan tumpukan kesalahan juga menampilkan bingkai yang terjadi sebelum titik asinkron diperkenalkan. Dengan penggunaan setTimeout, ini akan menangkap siapa yang memanggil fungsi setTimeout yang akhirnya menghasilkan kesalahan. Lihat http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/ untuk informasi lebih lanjut.
Pelacakan tumpukan async akan terlihat seperti:
throwError @ throw-error.js:2 setTimeout (async) throwErrorAsync @ throw-error.js:10 (anonymous function) @ throw-error-basic.html:14
Pelacakan tumpukan asinkron saat ini hanya didukung di Chrome DevTools, hanya untuk pengecualian yang muncul saat DevTools terbuka. Pelacakan tumpukan yang diakses dari objek Error dalam kode tidak akan memiliki pelacakan tumpukan asinkron sebagai bagiannya.
Dalam beberapa kasus, dimungkinkan untuk melakukan polyfill pelacakan tumpukan asinkron, namun hal ini dapat menyebabkan penurunan kinerja yang signifikan untuk aplikasi Anda karena menangkap pelacakan tumpukan tidaklah murah.
Hanya Chrome DevTools yang secara asli mendukung pelacakan tumpukan asinkron.
Pelacakan tumpukan untuk kode yang dievaluasi atau disisipkan ke dalam halaman HTML akan menggunakan URL halaman dan nomor baris/kolom untuk kode yang dieksekusi.
Misalnya:
at throwError (http://mknichel.github.io/javascript-errors/throw-error-basic.html:8:9) at http://mknichel.github.io/javascript-errors/throw-error-basic.html:12:3
Jika skrip ini sebenarnya berasal dari skrip yang disisipkan untuk alasan pengoptimalan, maka nomor URL, baris, dan kolomnya akan salah. Untuk mengatasi masalah ini, Chrome dan Firefox mendukung anotasi //# sourceURL=
(Safari, Edge, dan IE tidak). URL yang ditentukan dalam anotasi ini akan digunakan sebagai URL untuk semua pelacakan tumpukan, dan nomor baris dan kolom akan dihitung relatif terhadap awal tag , bukan dokumen HTML. Untuk error yang sama seperti di atas, menggunakan anotasi sourceURL dengan nilai "inline.js" akan menghasilkan pelacakan tumpukan yang terlihat seperti:
at throwError (http://mknichel.github.io/javascript-errors/inline.js:8:9) at http://mknichel.github.io/javascript-errors/inline.js:12:3
Ini adalah teknik yang sangat berguna untuk memastikan bahwa pelacakan tumpukan masih benar bahkan ketika menggunakan skrip inline dan eval.
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl menjelaskan anotasi sourceURL secara lebih rinci.
Safari, Edge, dan IE tidak mendukung anotasi sourceURL untuk memberi nama skrip inline dan evals. Jika Anda menggunakan skrip inline di IE atau Safari dan mengaburkan kode Anda, Anda tidak akan dapat melakukan deobfuscate kesalahan yang berasal dari skrip tersebut.
Hingga Chrome 42, Chrome tidak menghitung nomor baris dengan benar untuk skrip inline yang menggunakan anotasi sourceURL. Lihat https://bugs.chromium.org/p/v8/issues/detail?id=3920 untuk informasi lebih lanjut.
Nomor baris untuk bingkai tumpukan dari skrip sebaris salah ketika anotasi sourceURL digunakan karena relatif terhadap awal dokumen HTML dan bukan awal tag skrip sebaris (membuat deobfuscation yang benar tidak mungkin dilakukan). https://code.google.com/p/chromium/issues/detail?id=578269
Untuk kode yang menggunakan eval, ada perbedaan lain dalam pelacakan tumpukan selain menggunakan anotasi sourceURL atau tidak. Di Chrome, pelacakan tumpukan dari pernyataan yang digunakan di eval akan terlihat seperti:
Error: Error from eval at evaledFunction (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3),:1:36) at eval (eval at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3), :1:68) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
Di MS Edge dan IE11, ini akan terlihat seperti:
Error from eval at evaledFunction (eval code:1:30) at eval code (eval code:1:2) at evalError (http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3)
Di Safari:
Error from eval evaledFunction eval code eval@[native code] evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:7
dan di Firefox:
Error from eval evaledFunction@http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:36 @http://mknichel.github.io/javascript-errors/javascript-errors.js line 137 > eval:1:11 evalError@http://mknichel.github.io/javascript-errors/javascript-errors.js:137:3
Perbedaan ini dapat menyulitkan penguraian kode eval yang sama di semua browser.
Setiap browser menggunakan format pelacakan tumpukan yang berbeda untuk kesalahan yang terjadi di dalam eval.
Kode JavaScript Anda juga dapat dipanggil langsung dari kode asli. Array.prototype.forEach
adalah contoh yang bagus - Anda meneruskan fungsi ke forEach
dan mesin JS akan memanggil fungsi itu untuk Anda.
fungsi throwErrorWithNativeFrame() { var arr = [0, 1, 2, 3]; arr.forEach(fungsi bernamaFn(nilai) {throwError(); });}
Ini menghasilkan jejak tumpukan berbeda di browser berbeda. Chrome dan Safari menambahkan nama fungsi asli dalam pelacakan tumpukan itu sendiri sebagai bingkai terpisah, seperti:
(Chrome) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.forEach (native) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7) (Safari) namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:15 forEach@[native code] throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:14 (Edge) at namedFn (http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5) at Array.prototype.forEach (native code) at throwErrorWithNativeFrame (http://mknichel.github.io/javascript-errors/javascript-errors.js:152:7)
Namun, Firefox dan IE11 tidak menunjukkan bahwa forEach
dipanggil sebagai bagian dari tumpukan:
namedFn@http://mknichel.github.io/javascript-errors/javascript-errors.js:153:5 throwErrorWithNativeFrame@http://mknichel.github.io/javascript-errors/javascript-errors.js:152:3
Beberapa browser menyertakan bingkai kode asli dalam pelacakan tumpukan, sementara browser lainnya tidak.
Untuk mendeteksi bahwa aplikasi Anda mengalami kesalahan, beberapa kode harus mampu menangkap kesalahan tersebut dan melaporkannya. Ada beberapa teknik untuk menangkap kesalahan, masing-masing dengan kelebihan dan kekurangannya.
window.onerror
adalah salah satu cara termudah dan terbaik untuk mulai menangkap kesalahan. Dengan menetapkan window.onerror
ke suatu fungsi, kesalahan apa pun yang tidak tertangkap oleh bagian lain aplikasi akan dilaporkan ke fungsi ini, bersama dengan beberapa informasi tentang kesalahan tersebut. Misalnya:
window.onerror = function(pesan, url, baris, kolom, err) { console.log('Aplikasi mengalami kesalahan: ' + pesan); console.log('Jejak tumpukan: ' + err.stack);}
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror menjelaskan hal ini secara lebih rinci.
Secara historis, ada beberapa masalah dengan pendekatan ini:
Tidak ada objek Kesalahan yang disediakan
Argumen ke-5 pada fungsi window.onerror
seharusnya berupa objek Error. Ini telah ditambahkan ke spesifikasi WHATWG pada tahun 2013: https://html.spec.whatwg.org/multipage/webappapis.html#errorevent. Chrome, Firefox, dan IE11 sekarang menyediakan objek Error dengan benar (bersama dengan properti tumpukan kritis), namun Safari, MS Edge, dan IE10 tidak. Ini berfungsi di Firefox sejak Firefox 14 (https://bugzilla.mozilla.org/show_bug.cgi?id=355430) dan di Chrome sejak akhir 2013 (https://mikewest.org/2013/08/debugging-runtime-errors -dengan-window-onerror, https://code.google.com/p/chromium/issues/detail?id=147127). Safari 10 meluncurkan dukungan untuk objek Error di window.onerror.
Safari (versi di bawah 10), MS Edge, dan IE10 tidak mendukung objek Error dengan pelacakan tumpukan di window.onerror.
Sanitasi lintas domain
Di Chrome, kesalahan yang berasal dari domain lain di pengendali window.onerror akan disanitasi menjadi "Kesalahan skrip.", "", 0. Umumnya tidak masalah jika Anda benar-benar tidak ingin memproses kesalahan jika kesalahan tersebut berasal dari skrip yang tidak Anda pedulikan, sehingga aplikasi dapat menyaring kesalahan yang terlihat seperti ini. Namun, hal ini tidak terjadi di Firefox atau Safari atau IE11, Chrome juga tidak melakukan ini untuk blok coba/tangkap yang membungkus kode yang melanggar.
Jika Anda ingin menerima kesalahan di window.onerror
di Chrome dengan fidelitas penuh dari skrip lintas domain, sumber daya tersebut harus menyediakan header lintas asal yang sesuai. Lihat https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror untuk informasi lebih lanjut.
Chrome adalah satu-satunya browser yang akan membersihkan kesalahan yang berasal dari sumber lain. Berhati-hatilah saat memfilternya, atau atur header yang sesuai.
Ekstensi Chrome
Di Chrome versi lama, ekstensi Chrome yang dipasang di mesin pengguna juga dapat menimbulkan kesalahan yang dilaporkan ke window.onerror. Masalah ini telah diperbaiki pada versi Chrome yang lebih baru. Lihat bagian Ekstensi Chrome khusus di bawah.
API window.addEventListener("error")
berfungsi sama dengan API window.onerror. Lihat http://www.w3.org/html/wg/drafts/html/master/webappapis.html#runtime-script-errors untuk informasi lebih lanjut tentang pendekatan ini.
Menangkap kesalahan melalui window.onerror tidak mencegah kesalahan tersebut juga muncul di konsol DevTools. Kemungkinan besar ini adalah perilaku yang tepat untuk pengembangan karena pengembang dapat dengan mudah melihat kesalahannya. Jika Anda tidak ingin kesalahan ini muncul dalam produksi kepada pengguna akhir, e.preventDefault()
dapat dipanggil jika menggunakan pendekatan window.addEventListener.
window.onerror adalah alat terbaik untuk menangkap dan melaporkan kesalahan JS. Disarankan agar hanya kesalahan JS dengan objek Kesalahan valid dan jejak tumpukan yang dilaporkan kembali ke server, jika tidak, kesalahan mungkin sulit diselidiki atau Anda mungkin mendapatkan banyak spam dari ekstensi Chrome atau skrip lintas domain.
Mengingat bagian di atas, sayangnya tidak mungkin mengandalkan window.onerror
di semua browser untuk menangkap semua informasi kesalahan. Untuk menangkap pengecualian secara lokal, blok coba/tangkap adalah pilihan yang tepat. Dimungkinkan juga untuk menggabungkan seluruh file JavaScript dalam blok coba/tangkap untuk menangkap informasi kesalahan yang tidak dapat ditangkap dengan window.onerror. Hal ini memperbaiki situasi untuk browser yang tidak mendukung window.onerror, namun juga memiliki beberapa kelemahan.
Blok coba/tangkap tidak akan menangkap semua kesalahan dalam program, seperti kesalahan yang muncul dari blok kode asinkron melalui window.setTimeout
. Coba/tangkap dapat digunakan dengan Titik Masuk yang Dilindungi untuk membantu mengisi kekosongan.
blok coba/tangkap yang membungkus seluruh aplikasi tidak cukup untuk menangkap semua kesalahan.
Versi lama V8 (dan kemungkinan mesin JS lainnya), fungsi yang berisi blok coba/tangkap tidak akan dioptimalkan oleh kompiler (http://www.html5rocks.com/en/tutorials/speed/v8/). Chrome memperbaikinya di TurboFan (https://codereview.chromium.org/1996373002).
Sebuah "titik masuk" ke dalam JavaScript adalah API browser apa pun yang dapat memulai eksekusi kode Anda. Contohnya termasuk setTimeout
, setInterval
, pendengar acara, XHR, soket web, atau janji. Kesalahan yang muncul dari titik masuk ini akan ditangkap oleh window.onerror, tetapi di browser yang tidak mendukung objek Error penuh di window.onerror, diperlukan mekanisme alternatif untuk menangkap kesalahan ini karena metode coba/tangkap disebutkan di atas juga tidak akan menangkap mereka.
Untungnya, JavaScript memungkinkan titik masuk ini digabungkan sehingga blok coba/tangkap dapat disisipkan sebelum fungsi dipanggil untuk menangkap kesalahan apa pun yang ditimbulkan oleh kode.
Setiap titik masuk memerlukan kode yang sedikit berbeda untuk melindungi titik masuk tersebut, namun inti dari metodologinya adalah:
fungsi melindungiEntryPoint(fn) { return function protectedFn() {coba { return fn();} catch (e) { // Tangani kesalahan.} }}_oldSetTimeout = window.setTimeout;window.setTimeout = fungsi protectedSetTimeout(fn, waktu) { return _oldSetTimeout.call(window, protectedEntryPoint(fn), time);};
Sayangnya, kesalahan yang terjadi di Promises mudah sekali tidak teramati dan tidak dilaporkan. Kesalahan yang terjadi di Promise tetapi tidak ditangani dengan melampirkan penangan penolakan tidak dilaporkan di tempat lain - kesalahan tersebut tidak dilaporkan ke window.onerror
. Meskipun Promise melampirkan pengendali penolakan, kode itu sendiri harus melaporkan kesalahan tersebut secara manual agar dapat dicatat. Lihat http://www.html5rocks.com/en/tutorials/es6/promises/#toc-error-handling untuk informasi lebih lanjut. Misalnya:
jendela.onerror = function(...) { // Ini tidak akan pernah dipanggil oleh kode Promise.};var p = new Promise(...);p.then(function() { throw new Error("Kesalahan ini tidak akan ditangani di mana pun.");});var p2 = new Promise(...);p2.then(function() { throw new Error("Kesalahan ini akan ditangani dalam rantai.");}).catch(function(error) { // Tampilkan pesan kesalahan kepada pengguna // Kode ini harus melaporkan kesalahan secara manual agar dapat dicatat di server, jika berlaku.});
Salah satu pendekatan untuk menangkap lebih banyak informasi adalah dengan menggunakan Titik Masuk yang Dilindungi untuk menggabungkan pemanggilan metode Promise dengan coba/tangkap untuk melaporkan kesalahan. Ini mungkin terlihat seperti:
var _oldPromiseThen = Janji.prototipe.kemudian; Promise.prototype.then = fungsi protectedThen(callback, errorHandler) {return _oldPromiseThen.call(ini, proteksiEntryPoint(callback), proteksiEntryPoint(errorHandler)); };
Sayangnya, kesalahan dari Promises tidak akan tertangani secara default.
Implementasi Promise, seperti Q, Bluebird, dan Closure menangani kesalahan dengan cara berbeda yang lebih baik daripada penanganan kesalahan dalam implementasi Promises di browser.
Di Q, Anda dapat "mengakhiri" rantai Promise dengan memanggil .done()
yang akan memastikan bahwa jika kesalahan tidak ditangani dalam rantai, kesalahan tersebut akan ditampilkan kembali dan dilaporkan. Lihat https://github.com/kriskowal/q#handling-errors
Di Bluebird, penolakan yang tidak tertangani dicatat dan segera dilaporkan. Lihat http://bluebirdjs.com/docs/features.html#surfacing-unhandled-errors
Dalam implementasi goog.Promise Closure, penolakan yang tidak tertangani dicatat dan dilaporkan jika tidak ada rantai di Promise yang menangani penolakan dalam interval waktu yang dapat dikonfigurasi (untuk memungkinkan kode di kemudian hari dalam program menambahkan penangan penolakan).
Bagian pelacakan tumpukan async di atas membahas bahwa browser tidak menangkap informasi tumpukan ketika ada kait async, seperti memanggil Promise.prototype.then
. Polyfill yang dijanjikan menampilkan cara untuk menangkap titik jejak tumpukan asinkron yang dapat membuat diagnosis kesalahan menjadi lebih mudah. Pendekatan ini mahal, namun bisa sangat berguna untuk menangkap lebih banyak informasi debug.
Di Q, panggil Q.longStackSupport = true;
. Lihat https://github.com/kriskowal/q#long-stack-traces
Di Bluebird, panggil Promise.longStackTraces()
di suatu tempat dalam aplikasi. Lihat http://bluebirdjs.com/docs/features.html#long-stack-traces.
Di Penutupan, setel goog.Promise.LONG_STACK_TRACES
ke true.
Chrome 49 menambahkan dukungan untuk acara yang dikirimkan ketika Janji ditolak. Hal ini memungkinkan aplikasi untuk terhubung ke kesalahan Promise untuk memastikan bahwa kesalahan tersebut dilaporkan secara terpusat bersama dengan kesalahan lainnya.
window.addEventListener('penolakan tidak tertangani', kejadian => { // event.reason berisi alasan penolakan. Ketika Error muncul, ini adalah objek Error.});
Lihat https://googlechrome.github.io/samples/promise-rejection-events/ dan https://www.chromestatus.com/feature/4805872211460096 untuk informasi lebih lanjut.
Ini tidak didukung di browser lain mana pun.
Pekerja web, termasuk pekerja berdedikasi, pekerja bersama, dan pekerja layanan, menjadi lebih populer dalam aplikasi saat ini. Karena semua pekerja ini adalah skrip yang terpisah dari halaman utama, masing-masing memerlukan kode penanganan kesalahannya sendiri. Disarankan agar setiap skrip pekerja memasang kode penanganan kesalahan dan pelaporannya sendiri untuk efektivitas penanganan kesalahan yang maksimal dari pekerja.
Pekerja web khusus mengeksekusi dalam konteks eksekusi yang berbeda dari halaman utama, sehingga kesalahan dari pekerja tidak tertangkap oleh mekanisme di atas. Langkah-langkah tambahan perlu diambil untuk menangkap kesalahan dari pekerja di halaman.
Saat pekerja dibuat, properti onerror dapat disetel pada pekerja baru:
var pekerja = Pekerja baru('pekerja.js');pekerja.onerror = function(errorEvent) { ... };
Hal ini didefinisikan dalam https://html.spec.whatwg.org/multipage/workers.html#handler-abstractworker-onerror. Fungsi onerror
pada pekerja memiliki tanda tangan yang berbeda dengan window.onerror
yang dibahas di atas. Daripada menerima 5 argumen, worker.onerror
mengambil satu argumen: objek ErrorEvent
. API untuk objek ini dapat ditemukan di https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent. Ini berisi pesan, nama file, baris, dan kolom, tetapi tidak ada browser stabil saat ini yang berisi objek "Error" yang berisi jejak tumpukan (errorEvent.error adalah null). Karena API ini dijalankan dalam cakupan halaman induk, akan berguna jika menggunakan mekanisme pelaporan yang sama dengan halaman induk; sayangnya karena kurangnya pelacakan tumpukan, penggunaan API ini terbatas.
Di dalam JS yang dijalankan oleh pekerja, Anda juga dapat mendefinisikan API onerror yang mengikuti API window.onerror biasa: https://html.spec.whatwg.org/multipage/webappapis.html#onerroreventhandler. Dalam kode pekerja:
self.onerror = function(pesan, nama file, baris, kolom, error) { ... };
Pembahasan API ini sebagian besar mengikuti pembahasan di atas untuk window.onerror. Namun, ada 2 hal penting yang perlu diperhatikan:
Firefox dan Safari tidak melaporkan objek "kesalahan" sebagai argumen ke-5 pada fungsi tersebut, sehingga browser ini tidak mendapatkan jejak tumpukan dari pekerja (Chrome, MS Edge, dan IE11 mendapatkan jejak tumpukan). Titik Masuk yang Dilindungi untuk fungsi onmessage
dalam pekerja dapat digunakan untuk menangkap informasi pelacakan tumpukan untuk browser ini.
Karena kode ini dijalankan di dalam pekerja, kode harus memilih cara melaporkan kesalahan kembali ke server: Kode harus menggunakan postMessage
untuk menyampaikan kesalahan kembali ke halaman induk, atau menginstal mekanisme pelaporan kesalahan XHR (dibahas lebih lanjut di bawah) di pekerja itu sendiri.
Di Firefox, Safari, dan IE11 (tetapi tidak di Chrome), fungsi window.onerror
halaman induk juga akan dipanggil setelah onerror milik pekerja dan pendengar peristiwa onerror yang disetel oleh halaman tersebut dipanggil. Namun, window.onerror ini juga tidak akan berisi objek kesalahan dan oleh karena itu tidak akan memiliki jejak tumpukan juga. Browser ini juga harus berhati-hati untuk tidak melaporkan kesalahan dari pekerja berkali-kali.
Chrome dan Firefox mendukung SharedWorker API untuk berbagi pekerja di beberapa halaman. Karena pekerja dibagikan, pekerja tidak terikat pada satu halaman induk secara eksklusif; Hal ini menyebabkan beberapa perbedaan dalam cara penanganan kesalahan, meskipun SharedWorker sebagian besar mengikuti informasi yang sama dengan pekerja web khusus.
Di Chrome, ketika ada kesalahan dalam SharedWorker, hanya penanganan kesalahan pekerja itu sendiri dalam kode pekerja itu sendiri yang akan dipanggil (seperti jika mereka menyetel self.onerror
). window.onerror
halaman induk tidak akan dipanggil, dan Chrome tidak mendukung AbstractWorker.onerror
warisan yang dapat dipanggil di halaman induk seperti yang ditentukan dalam spesifikasi.
Di Firefox, perilaku ini berbeda. Kesalahan pada pekerja bersama akan menyebabkan window.onerror halaman induk dipanggil, tetapi objek kesalahan akan menjadi nol. Selain itu, Firefox mendukung properti AbstractWorker.onerror
, sehingga halaman induk dapat melampirkan penangan kesalahannya sendiri ke pekerja. Namun, ketika pengendali kesalahan ini dipanggil, objek kesalahan akan menjadi null sehingga tidak akan ada jejak tumpukan, sehingga penggunaannya terbatas.
Penanganan kesalahan untuk pekerja bersama berbeda-beda menurut browser.
Service Worker adalah spesifikasi baru yang saat ini hanya tersedia di versi Chrome dan Firefox terbaru. Para pekerja ini mengikuti diskusi yang sama seperti pekerja web khusus.
Pekerja layanan diinstal dengan memanggil fungsi navigator.serviceWorker.register
. Fungsi ini mengembalikan Promise yang akan ditolak jika terjadi kesalahan saat menginstal pekerja layanan, seperti kesalahan saat inisialisasi. Kesalahan ini hanya akan berisi pesan string dan tidak ada yang lain. Selain itu, karena Promise tidak melaporkan kesalahan ke penangan window.onerror
, aplikasi itu sendiri harus menambahkan blok catch ke Promise untuk menangkap kesalahan tersebut.
navigator.serviceWorker.register('pekerja-layanan-installation-error.js').catch(function(error) { // jenis kesalahan string});
Sama seperti pekerja lainnya, pekerja layanan dapat mengatur fungsi self.onerror
dalam pekerja layanan untuk menangkap kesalahan. Kesalahan instalasi pada pekerja layanan akan dilaporkan ke fungsi onerror, namun sayangnya kesalahan tersebut tidak berisi objek kesalahan atau pelacakan tumpukan.
API pekerja layanan berisi properti onerror yang diwarisi dari antarmuka abstractWorker, namun Chrome tidak melakukan apa pun