JavaScript Asinkron: Dari Neraka Panggilan Balik ke Asinkron dan Menunggu
Diterbitkan: 2022-03-11Salah satu kunci untuk menulis aplikasi web yang sukses adalah mampu membuat lusinan panggilan AJAX per halaman.
Ini adalah tantangan pemrograman asinkron yang khas, dan cara Anda memilih untuk menangani panggilan asinkron akan, sebagian besar, membuat atau menghancurkan aplikasi Anda, dan dengan ekstensi berpotensi seluruh startup Anda.
Menyinkronkan tugas asinkron dalam JavaScript adalah masalah serius untuk waktu yang sangat lama.
Tantangan ini memengaruhi pengembang back-end yang menggunakan Node.js sebanyak pengembang front-end yang menggunakan kerangka kerja JavaScript apa pun. Pemrograman asinkron adalah bagian dari pekerjaan kita sehari-hari, tetapi tantangannya sering dianggap enteng dan tidak dipertimbangkan pada waktu yang tepat.
Sejarah Singkat JavaScript Asychronous
Solusi pertama dan paling mudah datang dalam bentuk fungsi bersarang sebagai callback . Solusi ini menyebabkan sesuatu yang disebut callback hell , dan terlalu banyak aplikasi yang masih merasakan efeknya.
Kemudian, kami mendapat Janji . Pola ini membuat kode lebih mudah dibaca, tetapi jauh dari prinsip Jangan Ulangi Diri Sendiri (KERING). Masih terlalu banyak kasus di mana Anda harus mengulang potongan kode yang sama untuk mengelola alur aplikasi dengan benar. Penambahan terbaru, dalam bentuk pernyataan async/await, akhirnya membuat kode asinkron dalam JavaScript mudah dibaca dan ditulis seperti bagian kode lainnya.
Mari kita lihat contoh masing-masing solusi ini dan renungkan evolusi pemrograman asinkron dalam JavaScript.
Untuk melakukan ini, kami akan memeriksa tugas sederhana yang melakukan langkah-langkah berikut:
- Verifikasi nama pengguna dan kata sandi pengguna.
- Dapatkan peran aplikasi untuk pengguna.
- Log waktu akses aplikasi untuk pengguna.
Pendekatan 1: Neraka Panggilan Balik (“The Pyramid of Doom”)
Solusi kuno untuk menyinkronkan panggilan ini adalah melalui panggilan balik bersarang. Ini adalah pendekatan yang layak untuk tugas JavaScript asinkron sederhana, tetapi tidak akan diskalakan karena masalah yang disebut neraka panggilan balik.
Kode untuk tiga tugas sederhana akan terlihat seperti ini:
const verifyUser = function(username, password, callback){ dataBase.verifyUser(username, password, (error, userInfo) => { if (error) { callback(error) }else{ dataBase.getRoles(username, (error, roles) => { if (error){ callback(error) }else { dataBase.logAccess(username, (error) => { if (error){ callback(error); }else{ callback(null, userInfo, roles); } }) } }) } }) };
Setiap fungsi mendapat argumen yang merupakan fungsi lain yang dipanggil dengan parameter yang merupakan respons dari tindakan sebelumnya.
Terlalu banyak orang akan mengalami brain freeze hanya dengan membaca kalimat di atas. Memiliki aplikasi dengan ratusan blok kode yang serupa akan menyebabkan lebih banyak masalah bagi orang yang memelihara kode tersebut, bahkan jika mereka menulisnya sendiri.
Contoh ini menjadi lebih rumit setelah Anda menyadari bahwa database.getRoles
adalah fungsi lain yang memiliki panggilan balik bersarang.
const getRoles = function (username, callback){ database.connect((connection) => { connection.query('get roles sql', (result) => { callback(null, result); }) }); };
Selain memiliki kode yang sulit dipelihara, prinsip DRY sama sekali tidak ada nilainya dalam hal ini. Penanganan kesalahan, misalnya, diulang di setiap fungsi dan panggilan balik utama dipanggil dari setiap fungsi bersarang.
Operasi JavaScript asinkron yang lebih kompleks, seperti mengulang melalui panggilan asinkron, merupakan tantangan yang lebih besar. Faktanya, tidak ada cara sepele untuk melakukan ini dengan panggilan balik. Inilah sebabnya mengapa pustaka JavaScript Promise seperti Bluebird dan Q mendapat banyak daya tarik. Mereka menyediakan cara untuk melakukan operasi umum pada permintaan asinkron yang belum disediakan oleh bahasa itu sendiri.
Di situlah Janji JavaScript asli masuk.
Janji JavaScript
Janji adalah langkah logis berikutnya untuk lolos dari neraka panggilan balik. Metode ini tidak menghapus penggunaan callback, tetapi membuat rangkaian fungsi menjadi lebih mudah dan menyederhanakan kode, sehingga lebih mudah dibaca.

Dengan Promises di tempat, kode dalam contoh JavaScript asinkron kami akan terlihat seperti ini:
const verifyUser = function(username, password) { database.verifyUser(username, password) .then(userInfo => dataBase.getRoles(userInfo)) .then(rolesInfo => dataBase.logAccess(rolesInfo)) .then(finalResult => { //do whatever the 'callback' would do }) .catch((err) => { //do whatever the error handler needs }); };
Untuk mencapai kesederhanaan seperti ini, semua fungsi yang digunakan dalam contoh harus Promisified . Mari kita lihat bagaimana metode getRoles
akan diperbarui untuk mengembalikan Promise
:
const getRoles = function (username){ return new Promise((resolve, reject) => { database.connect((connection) => { connection.query('get roles sql', (result) => { resolve(result); }) }); }); };
Kami telah memodifikasi metode untuk mengembalikan Promise
, dengan dua callback, dan Promise
sendiri melakukan tindakan dari metode tersebut. Sekarang, resolve
dan reject
panggilan balik akan dipetakan ke metode Promise.then
dan Promise.catch
masing-masing.
Anda mungkin memperhatikan bahwa metode getRoles
secara internal masih rentan terhadap fenomena piramida malapetaka. Hal ini disebabkan cara metode database dibuat karena tidak mengembalikan Promise
. Jika metode akses database kami juga mengembalikan Promise
, metode getRoles
akan terlihat seperti berikut:
const getRoles = new function (userInfo) { return new Promise((resolve, reject) => { database.connect() .then((connection) => connection.query('get roles sql')) .then((result) => resolve(result)) .catch(reject) }); };
Pendekatan 3: Async/Menunggu
Piramida malapetaka secara signifikan dikurangi dengan diperkenalkannya Janji. Namun, kami masih harus mengandalkan callback yang diteruskan ke metode .then
dan .catch
dari Promise
.
Janji membuka jalan menuju salah satu peningkatan paling keren dalam JavaScript. ECMAScript 2017 membawa gula sintaksis di atas Janji dalam JavaScript dalam bentuk pernyataan async
dan await
.
Mereka memungkinkan kami untuk menulis kode berbasis Promise
seolah-olah itu sinkron, tetapi tanpa memblokir utas utama, seperti yang ditunjukkan oleh contoh kode ini:
const verifyUser = async function(username, password){ try { const userInfo = await dataBase.verifyUser(username, password); const rolesInfo = await dataBase.getRoles(userInfo); const logStatus = await dataBase.logAccess(userInfo); return userInfo; }catch (e){ //handle errors as needed } };
Menunggu Promise
untuk diselesaikan hanya diperbolehkan di dalam fungsi async
yang berarti verifyUser
harus didefinisikan menggunakan async function
.
Namun, setelah perubahan kecil ini dibuat, Anda dapat await
Promise
apa pun tanpa perubahan tambahan pada metode lain.
Async - Resolusi Janji yang Telah Lama Ditunggu
Fungsi asinkron adalah langkah logis berikutnya dalam evolusi pemrograman asinkron dalam JavaScript. Mereka akan membuat kode Anda lebih bersih dan lebih mudah dirawat. Mendeklarasikan suatu fungsi sebagai async
akan memastikan bahwa itu selalu mengembalikan Promise
sehingga Anda tidak perlu khawatir tentang itu lagi.
Mengapa Anda harus mulai menggunakan fungsi async
JavaScript hari ini?
- Kode yang dihasilkan jauh lebih bersih.
- Penanganan kesalahan jauh lebih sederhana dan bergantung pada
try
/catch
seperti pada kode sinkron lainnya. - Debug jauh lebih sederhana. Menyetel breakpoint di dalam blok
.then
tidak akan berpindah ke.then
berikutnya karena hanya melewati kode sinkron. Namun, Anda dapat melewati panggilanawait
seolah-olah itu adalah panggilan sinkron.