Penanganan Kesalahan Lebih Baik Di NodeJS Dengan Kelas Kesalahan

Diterbitkan: 2022-03-10
Ringkasan cepat Artikel ini ditujukan untuk pengembang JavaScript dan NodeJS yang ingin meningkatkan penanganan kesalahan dalam aplikasi mereka. Kelvin Omereshone menjelaskan pola kelas error dan cara menggunakannya untuk cara yang lebih baik dan efisien dalam menangani kesalahan di seluruh aplikasi Anda.

Penanganan kesalahan adalah salah satu bagian dari pengembangan perangkat lunak yang tidak cukup mendapatkan perhatian yang layak. Namun, membangun aplikasi yang kuat membutuhkan penanganan kesalahan dengan benar.

Anda dapat bertahan di NodeJS tanpa menangani kesalahan dengan benar, tetapi karena sifat NodeJS yang tidak sinkron, penanganan atau kesalahan yang tidak tepat dapat menyebabkan Anda sakit segera — terutama saat men-debug aplikasi.

Sebelum kita melanjutkan, saya ingin menunjukkan jenis kesalahan yang akan kita diskusikan bagaimana memanfaatkan kelas kesalahan.

Kesalahan Operasional

Ini adalah kesalahan yang ditemukan selama waktu program dijalankan. Kesalahan operasional bukanlah bug dan dapat terjadi dari waktu ke waktu sebagian besar karena satu atau kombinasi dari beberapa faktor eksternal seperti server basis data yang kehabisan waktu atau pengguna memutuskan untuk mencoba injeksi SQL dengan memasukkan kueri SQL di bidang input.

Di bawah ini adalah lebih banyak contoh kesalahan operasional:

  • Gagal terhubung ke server database;
  • Input yang tidak valid oleh pengguna (server merespons dengan 400 kode respons);
  • Minta batas waktu;
  • Sumber daya tidak ditemukan (server merespons dengan kode respons 404);
  • Server kembali dengan 500 respons.

Perlu juga dicatat untuk membahas secara singkat rekanan dari Kesalahan Operasional.

Kesalahan Pemrogram

Ini adalah bug dalam program yang dapat diatasi dengan mengubah kode. Jenis kesalahan ini tidak dapat ditangani karena terjadi sebagai akibat dari kode yang rusak. Contoh kesalahan tersebut adalah:

  • Mencoba membaca properti pada objek yang tidak ditentukan.
 const user = { firstName: 'Kelvin', lastName: 'Omereshone', } console.log(user.fullName) // throws 'undefined' because the property fullName is not defined
  • Memanggil atau memanggil fungsi asinkron tanpa panggilan balik.
  • Melewati string di mana angka diharapkan.

Artikel ini tentang penanganan Kesalahan Operasional di NodeJS. Penanganan kesalahan di NodeJS sangat berbeda dari penanganan kesalahan dalam bahasa lain. Ini karena sifat JavaScript yang asinkron dan keterbukaan JavaScript dengan kesalahan. Mari saya jelaskan:

Dalam JavaScript, instance dari kelas error bukan satu-satunya hal yang dapat Anda lempar. Anda benar-benar dapat membuang tipe data apa pun, keterbukaan ini tidak diizinkan oleh bahasa lain.

Misalnya, pengembang JavaScript dapat memutuskan untuk memasukkan angka alih-alih instance objek kesalahan, seperti:

 // bad throw 'Whoops :)'; // good throw new Error('Whoops :)')

Anda mungkin tidak melihat masalah dalam melempar tipe data lain, tetapi hal itu akan mengakibatkan proses debug yang lebih sulit karena Anda tidak akan mendapatkan jejak tumpukan dan properti lain yang diekspos oleh objek Kesalahan yang diperlukan untuk debug.

Mari kita lihat beberapa pola yang salah dalam penanganan kesalahan, sebelum melihat pola kelas Kesalahan dan bagaimana cara yang lebih baik untuk menangani kesalahan di NodeJS.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Pola Penanganan Kesalahan Buruk #1: Penggunaan Panggilan Balik yang Salah

Skenario dunia nyata : Kode Anda bergantung pada API eksternal yang memerlukan panggilan balik untuk mendapatkan hasil yang Anda harapkan.

Mari kita ambil cuplikan kode di bawah ini:

 'use strict'; const fs = require('fs'); const write = function () { fs.mkdir('./writeFolder'); fs.writeFile('./writeFolder/foobar.txt', 'Hello World'); } write();

Sampai NodeJS 8 dan di atasnya, kode di atas sah, dan pengembang hanya akan memecat dan melupakan perintah. Ini berarti pengembang tidak diharuskan untuk memberikan panggilan balik ke panggilan fungsi tersebut, dan karena itu dapat mengabaikan penanganan kesalahan. Apa yang terjadi ketika writeFolder belum dibuat? Panggilan untuk writeFile tidak akan dilakukan dan kami tidak akan tahu apa-apa tentangnya. Ini mungkin juga mengakibatkan kondisi balapan karena perintah pertama mungkin belum selesai ketika perintah kedua dimulai lagi, Anda tidak akan tahu.

Mari kita mulai menyelesaikan masalah ini dengan menyelesaikan kondisi balapan. Kami akan melakukannya dengan memberikan panggilan balik ke perintah pertama mkdir untuk memastikan direktori memang ada sebelum menulis dengan perintah kedua. Jadi kode kita akan terlihat seperti di bawah ini:

 'use strict'; const fs = require('fs'); const write = function () { fs.mkdir('./writeFolder', () => { fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); }); } write();

Meskipun kami memecahkan kondisi balapan, kami belum selesai. Kode kami masih bermasalah karena meskipun kami menggunakan panggilan balik untuk perintah pertama, kami tidak memiliki cara untuk mengetahui apakah folder writeFolder dibuat atau tidak. Jika folder tidak dibuat, maka panggilan kedua akan gagal lagi tetapi tetap saja, kami mengabaikan kesalahan itu lagi. Kami menyelesaikan ini dengan…

Penanganan Kesalahan Dengan Panggilan Balik

Untuk menangani kesalahan dengan benar dengan panggilan balik, Anda harus memastikan bahwa Anda selalu menggunakan pendekatan kesalahan-pertama. Artinya, Anda harus terlebih dahulu memeriksa apakah ada kesalahan yang dikembalikan dari fungsi sebelum melanjutkan untuk menggunakan data apa pun (jika ada) yang dikembalikan. Mari kita lihat cara yang salah untuk melakukan ini:

 'use strict'; // Wrong const fs = require('fs'); const write = function (callback) { fs.mkdir('./writeFolder', (err, data) => { if (data) fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); else callback(err) }); } write(console.log);

Pola di atas salah karena terkadang API yang Anda panggil mungkin tidak mengembalikan nilai apa pun atau mungkin mengembalikan nilai falsy sebagai nilai pengembalian yang valid. Ini akan membuat Anda berakhir dalam kasus kesalahan meskipun Anda mungkin memiliki panggilan fungsi atau API yang berhasil.

Pola di atas juga buruk karena penggunaannya akan memakan kesalahan Anda (kesalahan Anda tidak akan dipanggil meskipun itu mungkin terjadi). Anda juga tidak akan tahu apa yang terjadi dalam kode Anda sebagai akibat dari pola penanganan kesalahan semacam ini. Jadi cara yang tepat untuk kode di atas adalah:

 'use strict'; // Right const fs = require('fs'); const write = function (callback) { fs.mkdir('./writeFolder', (err, data) => { if (err) return callback(err) fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); }); } write(console.log);

Pola Penanganan Kesalahan yang Salah #2: Penggunaan Janji yang Salah

Skenario dunia nyata : Jadi Anda menemukan Janji dan Anda pikir itu jauh lebih baik daripada panggilan balik karena panggilan balik neraka dan Anda memutuskan untuk menjanjikan beberapa API eksternal yang menjadi basis kode Anda. Atau Anda menggunakan janji dari API eksternal atau API browser seperti fungsi fetch().

Saat ini kami tidak benar-benar menggunakan panggilan balik di basis kode NodeJS kami, kami menggunakan janji. Jadi mari kita implementasikan kembali kode contoh kita dengan sebuah janji:

 'use strict'; const fs = require('fs').promises; const write = function () { return fs.mkdir('./writeFolder').then(() => { fs.writeFile('./writeFolder/foobar.txt', 'Hello world!') }).catch((err) => { // catch all potential errors console.error(err) }) }

Mari kita letakkan kode di atas di bawah mikroskop — kita dapat melihat bahwa kita sedang mencabangkan janji fs.mkdir menjadi rantai janji lain (panggilan ke fs.writeFile) bahkan tanpa menangani panggilan janji itu. Anda mungkin berpikir cara yang lebih baik untuk melakukannya adalah:

 'use strict'; const fs = require('fs').promises; const write = function () { return fs.mkdir('./writeFolder').then(() => { fs.writeFile('./writeFolder/foobar.txt', 'Hello world!').then(() => { // do something }).catch((err) => { console.error(err); }) }).catch((err) => { // catch all potential errors console.error(err) }) }

Tapi di atas tidak akan skala. Ini karena jika kita memiliki lebih banyak rantai janji untuk dihubungi, kita akan berakhir dengan sesuatu yang mirip dengan neraka panggilan balik yang janji dibuat untuk dipecahkan. Ini berarti kode kita akan terus menjorok ke kanan. Kami akan memiliki janji neraka di tangan kami.

Menjanjikan API Berbasis Callback

Sering kali Anda ingin menjanjikan API berbasis panggilan balik sendiri untuk menangani kesalahan pada API itu dengan lebih baik. Namun, sebenarnya hal ini tidak mudah untuk dilakukan. Mari kita ambil contoh di bawah ini untuk menjelaskan alasannya.

 function doesWillNotAlwaysSettle(arg) { return new Promise((resolve, reject) => { doATask(foo, (err) => { if (err) { return reject(err); } if (arg === true) { resolve('I am Done') } }); }); }

Dari penjelasan di atas, jika arg tidak true dan kita tidak memiliki kesalahan dari panggilan ke fungsi doATask maka janji ini hanya akan hang out yang merupakan kebocoran memori di aplikasi Anda.

Kesalahan Sinkronisasi Tertelan Dalam Janji

Menggunakan konstruktor Promise memiliki beberapa kesulitan salah satunya adalah; segera setelah diselesaikan atau ditolak, ia tidak dapat memperoleh status lain. Ini karena janji hanya bisa mendapatkan satu status — baik itu tertunda atau diselesaikan/ditolak. Ini berarti kita dapat memiliki zona mati dalam janji kita. Mari kita lihat ini dalam kode:

 function deadZonePromise(arg) { return new Promise((resolve, reject) => { doATask(foo, (err) => { resolve('I'm all Done'); throw new Error('I am never reached') // Dead Zone }); }); }

Dari atas kita melihat begitu janji itu diselesaikan, baris berikutnya adalah zona mati dan tidak akan pernah tercapai. Ini berarti setiap penanganan kesalahan sinkron berikut yang dilakukan dalam janji Anda hanya akan ditelan dan tidak akan pernah dibuang.

Contoh Dunia Nyata

Contoh di atas membantu menjelaskan pola penanganan kesalahan yang buruk, mari kita lihat jenis masalah yang mungkin Anda lihat dalam kehidupan nyata.

Contoh Dunia Nyata #1 — Mengubah Kesalahan Menjadi String

Skenario : Anda memutuskan kesalahan yang dikembalikan dari API tidak cukup baik untuk Anda sehingga Anda memutuskan untuk menambahkan pesan Anda sendiri ke dalamnya.

 'use strict'; function readTemplate() { return new Promise(() => { databaseGet('query', function(err, data) { if (err) { reject('Template not found. Error: ', + err); } else { resolve(data); } }); }); } readTemplate();

Mari kita lihat apa yang salah dengan kode di atas. Dari atas kita melihat pengembang sedang mencoba untuk memperbaiki kesalahan yang dilemparkan oleh databaseGet API dengan menggabungkan kesalahan yang dikembalikan dengan string "Template tidak ditemukan". Pendekatan ini memiliki banyak kelemahan karena ketika penggabungan selesai, pengembang secara implisit menjalankan toString pada objek kesalahan yang dikembalikan. Dengan cara ini dia kehilangan informasi tambahan yang dikembalikan oleh kesalahan (ucapkan selamat tinggal pada jejak tumpukan). Jadi yang dimiliki developer saat ini hanyalah string yang tidak berguna saat debugging.

Cara yang lebih baik adalah dengan menjaga kesalahan seperti itu atau membungkusnya dengan kesalahan lain yang telah Anda buat dan melampirkan kesalahan yang dilemparkan dari panggilan databaseGet sebagai properti untuk itu.

Contoh Dunia Nyata #2: Sepenuhnya Mengabaikan Kesalahan

Skenario : Mungkin ketika pengguna mendaftar di aplikasi Anda, jika terjadi kesalahan, Anda hanya ingin menangkap kesalahan dan menampilkan pesan khusus tetapi Anda sepenuhnya mengabaikan kesalahan yang tertangkap bahkan tanpa mencatatnya untuk tujuan debugging.

 router.get('/:id', function (req, res, next) { database.getData(req.params.userId) .then(function (data) { if (data.length) { res.status(200).json(data); } else { res.status(404).end(); } }) .catch(() => { log.error('db.rest/get: could not get data: ', req.params.userId); res.status(500).json({error: 'Internal server error'}); }) });

Dari atas, kita dapat melihat bahwa kesalahan sepenuhnya diabaikan dan kode mengirim 500 ke pengguna jika panggilan ke database gagal. Namun pada kenyataannya penyebab kegagalan database mungkin adalah salah format data yang dikirim oleh pengguna yaitu error dengan kode status 400.

Dalam kasus di atas, kita akan berakhir dengan horor debugging karena Anda sebagai pengembang tidak akan tahu apa yang salah. Pengguna tidak akan dapat memberikan laporan yang layak karena 500 kesalahan server internal selalu terjadi. Anda akan menghabiskan waktu berjam-jam untuk menemukan masalah yang sama saja dengan membuang-buang waktu dan uang majikan Anda.

Contoh Dunia Nyata #3: Tidak Menerima Kesalahan yang Dilempar Dari API

Skenario : Kesalahan terjadi dari API yang Anda gunakan tetapi Anda tidak menerima kesalahan itu, sebaliknya Anda menyusun dan mengubah kesalahan dengan cara yang membuatnya tidak berguna untuk tujuan debugging.

Ambil contoh kode berikut di bawah ini:

 async function doThings(input) { try { validate(input); try { await db.create(input); } catch (error) { error.message = `Inner error: ${error.message}` if (error instanceof Klass) { error.isKlass = true; } throw error } } catch (error) { error.message = `Could not do things: ${error.message}`; await rollback(input); throw error; } }

Banyak yang terjadi dalam kode di atas yang akan mengarah pada debugging horor. Mari lihat:

  • Membungkus blok try/catch : Anda dapat melihat dari atas bahwa kami membungkus blok try/catch yang merupakan ide yang sangat buruk. Kami biasanya mencoba mengurangi penggunaan blok try/catch untuk mengecilkan permukaan di mana kami harus menangani kesalahan kami (anggap itu sebagai penanganan kesalahan KERING);
  • Kami juga memanipulasi pesan kesalahan dalam upaya untuk meningkatkan yang juga bukan ide yang baik;
  • Kami memeriksa apakah kesalahannya adalah turunan dari tipe Klass dan dalam kasus ini, kami menyetel properti boolean dari kesalahan isKlass ke truev(tetapi jika pemeriksaan itu lolos maka kesalahannya adalah tipe Klass );
  • Kami juga memutar kembali basis data terlalu dini karena, dari struktur kode, ada kecenderungan tinggi bahwa kami mungkin tidak mengenai basis data saat kesalahan terjadi.

Di bawah ini adalah cara yang lebih baik untuk menulis kode di atas:

 async function doThings(input) { validate(input); try { await db.create(input); } catch (error) { try { await rollback(); } catch (error) { logger.log('Rollback failed', error, 'input:', input); } throw error; } }

Mari kita analisis apa yang kita lakukan dengan benar dalam cuplikan di atas:

  • Kami menggunakan satu blok try/catch dan hanya di blok tangkap kami menggunakan blok try/catch lain yang berfungsi sebagai penjaga jika terjadi sesuatu dengan fungsi rollback itu dan kami mencatatnya;
  • Akhirnya, kami membuang kesalahan asli yang kami terima yang berarti kami tidak kehilangan pesan yang termasuk dalam kesalahan itu.

Pengujian

Kami sebagian besar ingin menguji kode kami (baik secara manual atau otomatis). Tetapi seringkali kita hanya menguji hal-hal positif. Untuk pengujian yang kuat, Anda juga harus menguji kesalahan dan kasus tepi. Kelalaian ini bertanggung jawab atas bug yang menemukan jalan mereka ke dalam produksi yang akan menghabiskan lebih banyak waktu debugging ekstra.

Tip : Selalu pastikan untuk menguji tidak hanya hal-hal positif (mendapatkan kode status 200 dari titik akhir) tetapi juga semua kasus kesalahan dan semua kasus tepi juga.

Contoh Dunia Nyata #4: Penolakan yang Tidak Tertangani

Jika Anda pernah menggunakan janji sebelumnya, Anda mungkin mengalami unhandled rejections .

Berikut ini adalah petunjuk singkat tentang penolakan yang tidak tertangani. Penolakan yang tidak ditangani adalah penolakan janji yang tidak ditangani. Ini berarti bahwa janji itu ditolak tetapi kode Anda akan terus berjalan.

Mari kita lihat contoh umum di dunia nyata yang mengarah pada penolakan yang tidak tertangani..

 'use strict'; async function foobar() { throw new Error('foobar'); } async function baz() { throw new Error('baz') } (async function doThings() { const a = foobar(); const b = baz(); try { await a; await b; } catch (error) { // ignore all errors! } })();

Kode di atas pada tampilan pertama mungkin tampak tidak rawan kesalahan. Namun jika dilihat lebih dekat, kita mulai melihat sebuah cacat. Mari saya jelaskan: Apa yang terjadi ketika a ditolak? Itu berarti await b tidak pernah tercapai dan itu berarti penolakan yang tidak tertangani. Solusi yang mungkin adalah menggunakan Promise.all pada kedua janji. Jadi kodenya akan berbunyi seperti ini:

 'use strict'; async function foobar() { throw new Error('foobar'); } async function baz() { throw new Error('baz') } (async function doThings() { const a = foobar(); const b = baz(); try { await Promise.all([a, b]); } catch (error) { // ignore all errors! } })();

Berikut adalah skenario dunia nyata lain yang akan menyebabkan kesalahan penolakan janji yang tidak tertangani:

 'use strict'; async function foobar() { throw new Error('foobar'); } async function doThings() { try { return foobar() } catch { // ignoring errors again ! } } doThings();

Jika Anda menjalankan cuplikan kode di atas, Anda akan mendapatkan penolakan janji yang tidak tertangani, dan inilah alasannya: Meskipun tidak jelas, kami mengembalikan janji (foobar) sebelum kami menanganinya dengan try/catch . Yang harus kita lakukan adalah menunggu janji yang sedang kita tangani dengan try/catch sehingga kodenya akan terbaca:

 'use strict'; async function foobar() { throw new Error('foobar'); } async function doThings() { try { return await foobar() } catch { // ignoring errors again ! } } doThings();

Mengakhiri Hal-Hal Negatif

Sekarang setelah Anda melihat pola penanganan kesalahan yang salah, dan kemungkinan perbaikan, sekarang mari selami pola kelas Kesalahan dan bagaimana hal itu memecahkan masalah penanganan kesalahan yang salah di NodeJS.

Kelas Kesalahan

Dalam pola ini, kami akan memulai aplikasi kami dengan kelas ApplicationError dengan cara ini kami tahu semua kesalahan dalam aplikasi kami yang kami lempar secara eksplisit akan mewarisi darinya. Jadi kita akan memulai dengan kelas kesalahan berikut:

  • ApplicationError
    Ini adalah nenek moyang dari semua kelas kesalahan lainnya yaitu semua kelas kesalahan lainnya mewarisi dari itu.
  • DatabaseError
    Setiap kesalahan yang berkaitan dengan operasi Database akan mewarisi dari kelas ini.
  • UserFacingError
    Setiap kesalahan yang dihasilkan sebagai hasil dari interaksi pengguna dengan aplikasi akan diwarisi dari kelas ini.

Berikut adalah bagaimana file kelas error kami akan terlihat seperti:

 'use strict'; // Here is the base error classes to extend from class ApplicationError extends Error { get name() { return this.constructor.name; } } class DatabaseError extends ApplicationError { } class UserFacingError extends ApplicationError { } module.exports = { ApplicationError, DatabaseError, UserFacingError }

Pendekatan ini memungkinkan kita untuk membedakan kesalahan yang dilemparkan oleh aplikasi kita. Jadi sekarang jika kita ingin menangani kesalahan permintaan yang buruk (input pengguna tidak valid) atau kesalahan tidak ditemukan (sumber daya tidak ditemukan) kita dapat mewarisi dari kelas dasar yaitu UserFacingError (seperti pada kode di bawah).

 const { UserFacingError } = require('./baseErrors') class BadRequestError extends UserFacingError { constructor(message, options = {}) { super(message); // You can attach relevant information to the error instance // (eg. the username) for (const [key, value] of Object.entries(options)) { this[key] = value; } } get statusCode() { return 400; } } class NotFoundError extends UserFacingError { constructor(message, options = {}) { super(message); // You can attach relevant information to the error instance // (eg. the username) for (const [key, value] of Object.entries(options)) { this[key] = value; } } get statusCode() { return 404 } } module.exports = { BadRequestError, NotFoundError }

Salah satu manfaat dari pendekatan kelas error adalah jika kita membuang salah satu kesalahan ini, misalnya, NotFoundError , setiap pengembang yang membaca basis kode ini akan dapat memahami apa yang sedang terjadi saat ini (jika mereka membaca kode ).

Anda akan dapat meneruskan beberapa properti khusus untuk setiap kelas kesalahan juga selama instantiasi kesalahan itu.

Manfaat utama lainnya adalah Anda dapat memiliki properti yang selalu menjadi bagian dari kelas kesalahan, misalnya, jika Anda menerima kesalahan UserFacing, Anda akan tahu bahwa kode status selalu menjadi bagian dari kelas kesalahan ini sekarang Anda dapat langsung menggunakannya di kode nanti.

Tips Memanfaatkan Kelas Kesalahan

  • Buat modul Anda sendiri (mungkin yang pribadi) untuk setiap kelas kesalahan dengan cara itu Anda cukup mengimpornya di aplikasi Anda dan menggunakannya di mana saja.
  • Buang hanya kesalahan yang Anda pedulikan (kesalahan yang merupakan turunan dari kelas kesalahan Anda). Dengan cara ini Anda tahu kelas kesalahan Anda adalah satu-satunya Sumber Kebenaran Anda dan berisi semua informasi yang diperlukan untuk men-debug aplikasi Anda.
  • Memiliki modul kesalahan abstrak cukup berguna karena sekarang kita mengetahui semua informasi yang diperlukan mengenai kesalahan yang dapat ditimbulkan oleh aplikasi kita berada di satu tempat.
  • Menangani kesalahan berlapis-lapis. Jika Anda menangani kesalahan di mana-mana, Anda memiliki pendekatan yang tidak konsisten untuk penanganan kesalahan yang sulit untuk dilacak. Dengan lapisan yang saya maksud seperti database, lapisan express/fastify/HTTP, dan sebagainya.

Mari kita lihat bagaimana kelas kesalahan terlihat dalam kode. Berikut adalah contoh dalam ekspres:

 const { DatabaseError } = require('./error') const { NotFoundError } = require('./userFacingErrors') const { UserFacingError } = require('./error') // Express app.get('/:id', async function (req, res, next) { let data try { data = await database.getData(req.params.userId) } catch (err) { return next(err); } if (!data.length) { return next(new NotFoundError('Dataset not found')); } res.status(200).json(data) }) app.use(function (err, req, res, next) { if (err instanceof UserFacingError) { res.sendStatus(err.statusCode); // or res.status(err.statusCode).send(err.errorCode) } else { res.sendStatus(500) } // do your logic logger.error(err, 'Parameters: ', req.params, 'User data: ', req.user) });

Dari penjelasan di atas, kami memanfaatkan bahwa Express mengekspos penangan kesalahan global yang memungkinkan Anda menangani semua kesalahan di satu tempat. Anda dapat melihat panggilan ke next() di tempat kami menangani kesalahan. Panggilan ini akan meneruskan kesalahan ke handler yang didefinisikan di bagian app.use . Karena express tidak mendukung async/await, kami menggunakan blok try/catch .

Jadi dari kode di atas, untuk menangani error kita hanya perlu mengecek apakah error yang dilempar adalah instance UserFacingError dan secara otomatis kita tahu bahwa akan ada statusCode di objek error dan kita kirimkan ke pengguna (Anda mungkin ingin untuk memiliki kode kesalahan tertentu yang dapat Anda berikan ke klien) dan itu saja.

Anda juga akan melihat bahwa dalam pola ini ( pola kelas error ) setiap kesalahan lain yang tidak Anda lempar secara eksplisit adalah kesalahan 500 karena itu adalah sesuatu yang tidak terduga yang berarti Anda tidak secara eksplisit membuang kesalahan itu dalam aplikasi Anda. Dengan cara ini, kami dapat membedakan jenis kesalahan yang terjadi di aplikasi kami.

Kesimpulan

Penanganan kesalahan yang tepat dalam aplikasi Anda dapat membuat Anda tidur lebih nyenyak di malam hari dan menghemat waktu debug. Berikut adalah beberapa poin penting yang dapat diambil dari artikel ini:

  • Gunakan kelas kesalahan yang secara khusus disiapkan untuk aplikasi Anda;
  • Menerapkan penangan kesalahan abstrak;
  • Selalu gunakan async/menunggu;
  • Buat kesalahan menjadi ekspresif;
  • Pengguna berjanji jika perlu;
  • Kembalikan status dan kode kesalahan yang tepat;
  • Manfaatkan kait janji.

Smashing Cat mengeksplorasi wawasan baru, tentu saja di Smashing Workshops.

Bit front-end & UX yang berguna, dikirimkan seminggu sekali.

Dengan alat untuk membantu Anda menyelesaikan pekerjaan dengan lebih baik. Berlangganan dan dapatkan PDF Daftar Periksa Desain Antarmuka Cerdas Vitaly melalui email.

Di front-end & UX. Dipercaya oleh 190.000 orang.