Pengantar Asynchronous Process

 

Pengantar Asynchronous Process

Hello, welcome to this module! Asynchronous process adalah topik yang sangat esensial untuk dipahami sebagai developer JavaScript. Sebagai bocoran, materi ini akan mengajarkan Anda bahwa kode JavaScript tidak harus selalu ditunggu hingga kelar agar kode yang mengikutinya dapat dieksekusi. Maknanya, antrean kode setelahnya akan tetap berjalan sembari menunggu dan mengharapkan hasil dari kode sebelumnya. Menarik, ya?

Meskipun terbilang topik baru, pembahasan ini akan kami bungkus agar Anda dapat belajar dengan mudah dan nyaman. Berikut adalah beberapa poin yang akan Anda miliki setelah menghabiskan materi ini.

  • Mampu menentukan perbedaan alur proses yang berjalan secara asynchronous dan synchronous.
  • Mampu membilang workload yang dijalankan secara asynchronous dalam kasus nyata.
  • Mampu mengilustrasikan sebuah proses yang berjalan secara asynchronous menggunakan setTimeout.
  • Mampu menggunakan callback, Promise, dan async-await dalam menangani proses asynchronous secara paralel ataupun serial.
  • Mampu menggunakan Promise.all dan Promise.allSettled untuk menjalankan banyak Promise sekaligus.

Apa Itu Asynchronous Process

Dalam pengembangan aplikasi web atau Node.js, menangani proses yang berjalan secara asynchronous menjadi topik yang cukup menantang. Lalu, apa sih proses asynchronous atau asinkron itu?

Dalam KBBI, asinkron berarti tidak dalam waktu atau kecepatan yang sama atau tidak serentak. Jika dimaknai dalam konteks pemrograman, proses atau operasi asinkron adalah sebuah operasi yang memungkinkan dijalankan oleh mesin dan kemudian dapat beralih fokus untuk menjalankan tugas-tugas (operasi) berikutnya sembari menunggu operasi sebelumnya selesai. Apa alasan adanya proses seperti ini?

Bayangkan Anda memiliki suatu tugas yang berpotensi mengonsumsi banyak waktu dan tidak seharusnya selalu ditunggu agar tugas lain bisa berjalan.


dos-ce4c49037fbad4cbb8025010ccfebf2d20240731134347.jpeg

Jika dicontohkan, ada banyak proses yang berjalan secara asinkron. Bahkan, tidak terhitung angkanya. Katakanlah proses yang terjadi dalam kehidupan kita sebagai manusia. Disadari ataupun tidak, kita sering mengalami proses ini. Menjaga kebersihan di lingkungan rumah menjadi salah satu contoh nyata. Misalnya, kita dihadapkan kepada beberapa tugas berikut.

  1. Mencuci baju dengan mesin cuci.
  2. Mengelap ruangan dapur.
  3. Menjalankan dishwasher untuk piring dan gelas kotor.
  4. Menyapu dan mengepel lantai.

Untuk mengerjakan tugas pertama, berapa waktu yang dibutuhkan oleh mesin cuci agar selesai? Apakah 10, 20, atau bahkan 50 menit? Berapa pun lama waktunya, mesin cuci dapat menyita waktu bagi kita. Gambaran kita saat ini adalah tugas berikutnya tidak akan dimulai sebelum tugas sebelumnya selesai. Namun, kenyataannya tidak!

Sebab tugas telah didelegasikan kepada mesin cuci dan tinggal menunggu, kita bisa mengerjakan tugas kedua dalam waktu yang bersamaan. Begitu juga mirip dengan beberapa tugas berikutnya jika bisa dikerjakan dalam satu waktu. Inilah yang disebut dengan proses asinkron.

Dengan konsep yang sama, pengembangan aplikasi web juga memanfaatkan operasi-operasi yang berjalan secara asinkron. Mesin dapat menjalankan tugas lainnya sembari menunggu proses asinkron selesai. Beberapa contoh operasinya seperti melakukan koneksi dengan jaringan (network request), menjalankan kueri ke basis data (querying a database), melakukan baca-tulis berkas dalam file system, dan operasi lainnya yang berpotensi mengonsumsi banyak waktu.

Jadi, apa kesimpulan dari proses asynchronous? Proses yang tidak melakukan blocking process terhadap proses berikutnya karena tugas komputasi yang besar dan memakan banyak waktu. Lawan dari proses tersebut adalah synchronous. Jika synchronous process adalah proses yang dijalankan secara berurutan, mulai dari awal sampai akhir, asynchronous process adalah proses yang dapat dieksekusi secara paralel.

Perhatikan ilustrasi berikut untuk melihat perbedaan antara synchronous dan asynchronous process.


dos-e50877d13864ba2ddd105288b5d87a1520240731134347.jpeg

Keren! Kali ini Anda sudah paham maksud dari asynchronous process. Pada beberapa materi ke depan, kita akan belajar metode penanganan asynchronous process dalam JavaScript, mulai dari cara lama hingga terkini.

Oke, sudahkah Anda siap? Mari kita mulai.

Simulasi Asynchronous Proses

Ada banyak sekali kejadian dari asynchronous process, terutama dalam web development. Kita sudah mengetahui beberapa contoh dari masalah ini dalam materi sebelumnya. Selama pembelajaran ini, kita belum pernah mempelajari dan memerintahkan mesin untuk mengemban tugas besar, seperti berkoneksi dengan network. Langkah awalnya, kita akan memanfaatkan satu global function dari JavaScript sebagai simulasi untuk menciptakan proses asinkron, yaitu setTimeout.

setTimeout adalah salah satu dari sekian global function yang dapat menetapkan timer (pengatur waktu) bagi function agar dieksekusi. Jadi, ada dua parameter wajib, yaitu nilai berupa function dan number sebagai timer. Setelah timer sudah berakhir, seluruh cuplikan kode dalam function akan dieksekusi.

Simpelnya, perhatikan contoh berikut.

Apakah Anda sudah menjalankan kode di atas? Program di atas seharusnya cukup mewakili dalam memahami cara kerja setTimeout. Tentunya Anda bisa membuktikan bahwa teks “Hello, world!” akan muncul setelah sepuluh detik, kan?

Lalu, manakah sisi dari proses asinkronnya? Mari kita periksa contoh kasus lain. Coba tebak hasil yang akan dikeluarkan dari cuplikan kode berikut.

Ekspektasi yang kita miliki adalah output dengan urutan berikut.

  1. Saya memesan kopi di kafe.
  2. Mohon menunggu. Pramusaji sedang membuatkan kopi dalam 5 detik.
  3. Pramusaji selesai membuat kopi.
  4. Pramusaji memberikan kopi pesanan.
  5. Saya mendapatkan kopi dan menghabiskannya.

Apakah ekspektasi sesuai dengan kenyataan? Silakan jalankan interactive code di atas untuk mengetahuinya.

Sebagaimana hasil yang diberikan oleh mesin, perintah console.log dalam makeCoffee dijalankan belakangan. Ini ditandai dengan teks “Pramusaji selesai membuat kopi.” muncul di akhir. Bagaimana bisa pramusaji memberikan kopi dan kita menghabiskannya jika kopinya saja masih dalam proses pembuatan? Tentunya ini tidak sesuai dengan urutan kode (sequential order) dan beginilah perilaku dari asynchronous process. Ingat! Ini kita simulasikan dengan setTimeout.

Bagi kita yang terbiasa dengan sequential order, ini terlihat aneh. Namun, kita bisa membuktikan bahwa proses yang memakan waktu lama (asynchronous) tidak melakukan blocking process dan kode-kode setelahnya tetap dapat dijalankan sembari proses asinkron diselesaikan.

Demi kemudahan membaca kode, Anda dapat memahami gambar berikut yang menunjukkan alur jalannya program.

dos-3897815fb0e93977b6cc831e1059f1b720240731134720.jpeg

Pada gambar di atas, kita menemukan ada sembilan langkah yang akan terjadi. Ada satu titik lagi yang ditandai dengan “?”. Apakah maksudnya langkah kesepuluh? Belum tentu atau bahkan tidak. Karena berjalan secara asinkron, kode tersebut dapat dijalankan kapan pun hingga proses asinkron selesai tanpa memedulikan urutan jalannya.

Sampai sini, mungkin Anda berpikir, “Bagaimana jika estimationTime dibuat 0 saja agar tidak ada waktu tunggu?”, dan seharusnya hasil akan sesuai dengan ekspektasi. Kenyataannya, tidak akan ada perbedaan hasil karena bagaimanapun setTimeout akan berjalan secara asinkron. Silakan ubah saja nilai dari variabel estimationTime dan perhatikan hasilnya.

Lalu, bagaimana solusinya agar kopi kita bisa diterima dan dihabiskan setelah pramusaji menyelesaikan pekerjaannya? Kita bisa memanfaatkan callback dan Promise. Oke, mari kita pelajari callback dahulu dengan lanjut ke materi berikutnya.

Penanganan dengan Callback

Salah satu metode untuk menangani asynchronous process adalah menggunakan callback. Namun, kita harus paham dahulu makna dari callback. Apa itu?

Callback adalah sebuah function yang dijadikan sebagai nilai argument bagi function yang lain. Ini persis dengan hal yang pernah kita pelajari pada materi Function Expression. Contoh callback yang pernah kita temui sebelumnya adalah memberikan function pada setTimeout dalam parameter pertama. Setelah mencapai nilai timeout, callback akan dibangkitkan atau dijalankan.

Jadi, bagaimana implementasi callback dalam menangani proses asinkron? Perhatikan hasil pengembangan contoh kode sebelumnya pada kode berikut.

Pada kode di atas, kita memiliki beberapa perubahan. makeCoffee memiliki satu parameter yang akan menerima function alias callback agar dijalankan setelah proses pembuatan kopi selesai. Function apa yang akan kita masukkan? Function tersebut adalah tugas untuk pramusaji memberikan kopi kepada customer dan kemudian customer menghabiskan kopinya. Hal yang pasti adalah kita berhasil memastikan bahwa suatu tugas benar-benar dijalankan hanya ketika proses asinkron selesai. Inilah callback untuk penanganan asynchronous process.

Mungkin ada yang berpikir bahwa proses pemberian kopi juga berupa asinkron dan kita masih bisa melanjutkan tugas lain pada waktu itu. Pramusaji tentunya membutuhkan waktu untuk memindahkan kopi ke cangkir dan mempercantiknya serta perlu waktu untuk berjalan menuju meja customer. Tepat! Mari kita sesuaikan kodenya seperti kode berikut.

Sip! Studi kasus kita semakin lengkap dalam mencakup berbagai persoalan. Anda dapat bereksplorasi dengan berbagai hal ketika penanganan menggunakan callback function ini.

Penanganan Error dengan Callback

Tugas yang berjalan secara asynchronous bisa saja berjalan sesuai dengan harapan dan bisa juga memberikan hasil bertolak belakang. Ketika kita menaruh ekspektasi terhadap pihak tertentu atas suatu tugas, mungkin ada benih-benih kekecewaan yang muncul dalam hati, antara keberhasilan atau kegagalan. Ini adalah pembicaraan mengenai kehidupan kita sebagai manusia. Tidak bisa dimungkiri juga mesin dapat mengalami hal yang sama.


dos-6c02b42602ebeba22d226c462d9bf98420240731135110.jpeg

Kita sudah mengerti ada banyak proses asynchronous yang dapat terjadi. Dalam konteks pengembangan aplikasi, ada kalanya kita gagal berkomunikasi dengan network, menjalankan kueri pada database, membaca file system, dsb. Mungkin ada beberapa hal yang menjadi penyebab, seperti halnya tidak memiliki koneksi internet. Bagaimana kita bisa berkomunikasi dengan network?

Developer aplikasi JavaScript harus dapat memperhatikan dan mengantisipasi kemungkinan kegagalan atau keberhasilan pada proses asinkron. Perhatikan kode berikut.

Mari lanjutkan studi kasus pemesanan kopi. Kita menambahkan fitur agar customer bisa memesan jenis kopi yang diinginkan. Misalnya kopi espresso. Lalu, kita menambahkan dua buah argumen pada callback function untuk membawa data jika proses asinkron berhasil atau gagal. Data error terletak pada argumen pertama dan data keberhasilan terletak pada argumen kedua.

Kemudian, bagaimana cara kita menentukan bahwa pembuatan kopi berhasil atau gagal? Silakan perhatikan potongan kode berikut.

  1. setTimeout(() => {
  2.   // Penentuan hasil dari proses asinkron
  3.   const number = Math.random();
  4.   if (number > 0.3) {
  5.     isSuccess = true;
  6.   }
  7.  
  8.   if (!isSuccess) {
  9.     callback(new Error('Gagal membuatkan kopi.'), null);
  10.     return;
  11.   }
  12.  
  13.   console.log('Pramusaji selesai membuat kopi.');
  14.   callback(null, name);
  15. }, estimationTime);

Dalam callback setTimeout, di sanalah penentuan hasil akan ditetapkan. Yes, isSuccess menjadi penentunya. Karena merupakan simulasi, kali ini kita tentukan keberhasilannya dengan meminta nilai angka random dari Math.random. Jika berhasil alias isSuccess bernilai true, kita bawakan data pada parameter kedua dari keberhasilan proses pembuatan kopi dan berikan null pada parameter pertama untuk menandakan bahwa tidak ada error yang terjadi. Jika gagal, nilai argumen akan sebaliknya.

Berikut adalah hasil jika program di atas kita jalankan.

  1. Pramusaji memberikan Kopi Espresso pesanan.
  2. Saya mendapatkan Kopi Espresso dan menghabiskannya.

Apa hasil yang diperoleh jika proses pembuatan kopi gagal? Berikut output-nya.
dos-a11b30f5b6a8da497992f352c9004c5020240731140209.jpeg

Jika ingin melihatnya secara langsung, Anda bisa jalankan program di atas beberapa kali hingga mendapati isSuccess bernilai false.

Nice! Itulah cara pertama untuk menangani proses asynchronous dalam JavaScript. Simulasi hanyalah simulasi, bukan sebuah kasus nyata. Supaya memiliki pengalaman yang berbeda, mari kita coba lihat penerapan callback pada proses asynchronous dengan memanfaatkan salah satu Node.js API, yaitu fs untuk membaca file system.

Jika dijalankan, seharusnya akan menampilkan teks berikut pada console.

  1. Hello Dicoding, my name is JavaScript.

Mantap! Pada contoh program di atas, kita melihat program JavaScript sedang berusaha membaca berkas bernama sample.txt. Ini ditandai dengan mesin membaca method readFile dari module yang bernama fs. Ini adalah salah satu built-in module (module bawaan) dari Node.js untuk membaca berkas-berkas dalam sistem operasi.

Hal yang menjadi titik bahasan adalah method readFile memanfaatkan callback untuk menangani proses asinkron. Jika pembacaan berkas berhasil, kita dapat melihat isi dari sample.txt pada console. Inilah yang kita pelajari saat ini!

Kini, Anda sudah memiliki gambaran cara penanganan asynchronous process dengan callback. Berikutnya, kita akan mempelajari metode kedua dalam menangani proses ini, yaitu Promise!

Apa Itu Promise

Callback bukanlah satu-satunya cara penanganan proses asynchronous sebenarnya. Callback dapat menjalankan tugasnya dengan sangat baik. Namun, ada hal yang menyebabkan callback ini mencapai taraf tidak efektif. Bayangkan saja bila suatu tugas bergantung terhadap hasil dua atau lebih tugas asynchronous seperti studi kasus kita sebelumnya.

  1. makeCoffee(order, (makeCoffeeError, makeCoffeeData) => {
  2.   if (makeCoffeeError) {
  3.     console.log(makeCoffeeError);
  4.     return;
  5.   }
  6.  
  7.   sendCoffee(makeCoffeeData, (sendCoffeeError, sendCoffeeData) => {
  8.     if (sendCoffeeError) {
  9.       console.log(sendCoffeeError);
  10.       return;
  11.     }
  12.  
  13.     console.log(`Pramusaji memberikan ${sendCoffeeData} pesanan.`);
  14.     console.log(`Saya mendapatkan ${sendCoffeeData} dan menghabiskannya.`);
  15.   });
  16. });
  17.  

Tentunya kode di atas baik-baik saja. Katakanlah aktivitas meminum kopi ditandai dengan munculnya teks “Saya mendapatkan Kopi Espresso dan menghabiskannya”. Hal yang menjadi permasalahan adalah bagaimana jika proses minum kopi bergantung pada lebih dari dua, tiga, empat, atau lebih async process? Dalam konteks penulisan kode, itu akan makin menjorok ke dalam dan lebih sulit dipahami. Inilah efek dari pemanfaatan callback dan akan merujuk pada istilah callback hell.

dos-e45457fcf769d1f28e90cec7240f084b20240731140634.jpeg

Teknik terkini yang disajikan oleh JavaScript adalah Promise. Promise adalah sebuah objek khusus yang akan menentukan keberhasilan atau kegagalan dari proses asynchronous. Secara bahasa, Promise memiliki arti janji dan memang konsep yang dianut sangat mirip dengan makna tersebut.

Promise bekerja dengan tiga buah state atau kondisi.

  • Pending: kondisi awal sebuah proses berjalan. Belum ada hasil yang diharapkan.
  • Fulfilled: kondisi keberhasilan proses dan akan mengembalikan nilai positif. Misalnya mengembalikan isi berkas jika pembacaannya sukses.
  • Rejected: operasi terjadi kegagalan dan membawa alasan atau data mengenai masalah ini. Biasanya, data kegagalan berupa instance dari class Error.

Dari ketiga kondisi Promise, ini mirip dengan kehidupan nyata. Pending menandakan sebuah janji sedang berproses untuk menuju state diselesaikan, baik itu fulfilled maupun rejected. Masuk ke kategori fulfilled untuk menandakan sebuah janji ditepati dan memberikan hasil kesuksesannya serta rejected untuk menandakan sebuah janji teringkari dan memberikan alasannya.

dos-ac4f3b702927ae5899a9bb458c4ef30920240731140813.jpeg

Mari kita ambil contoh tugas mencuci pakaian pada mesin cuci. Bayangkan saja mesin cuci punya tiga buah state.

  • Pending: mesin cuci sedang berjalan dan belum bisa memberikan hasil apa pun.
  • Fulfilled: mesin cuci menyelesaikan tugasnya dengan baik dan pakaian sudah dibersihkan.
  • Rejected: mesin cuci mengalami kegagalan ketika berproses dan mengembalikan baju kotor beserta alasannya. Misalnya belum diberi sabun, air tiba-tiba tidak mengalir, dan berbagai faktor lainnya.

Promise pada akhirnya akan diselesaikan. Tugas kita sebagai developer adalah memberikan kode logika untuk menangani jika proses masuk ke fulfilled atau terjadi kesalahan (rejected).

Sip! Anda sudah paham mengenai konsep Promise dalam JavaScript dan berikutnya kita akan praktik!

Penanganan dengan Promise

Sebelumnya, Anda sudah tahu efek di balik penggunaan callback yang makin kompleks. Ya, callback hell akan muncul. Promise dapat memberikan kode yang lebih simpel dan mudah dipahami. Mari kita mulai penerapan ini.

Anda masih ingat cara penggunaan callback, kan?

  1. lakukanSesuatu((lakukanSesuatuKesalahan, lakukanSesuatuData) => {
  2.   jika (lakukan sesuatu yang salah) {
  3.     konsol.log(doSomethingError);
  4.   }
  5.   konsol.log(doSomethingData);
  6. });

Dari contoh kode di atas, kita bisa menyederhanakannya seperti berikut dengan Promise.

  1. fungsi onFulfilled(doSomethingData) {
  2.   // Lakukan pekerjaanmu saat "terpenuhi"...
  3.   konsol.log(doSomethingData);
  4. }
  5. fungsi onRejected(doSomethingError) {
  6.   // Lakukan pekerjaan Anda ketika "ditolak" terjadi…
  7.   konsol.log(doSomethingError);
  8. }
  9. lakukanSesuatu().lalu(onTerpenuhi, onDitolak);

Lebih enak dibaca, ya? Ada sesuatu yang baru di sini, yaitu then. then adalah method khusus milik objek Promise. Ia yang akan menangani atau menerima hasil dari proses asinkron. Method ini menerima dua buah callback, yaitu callback untuk menangani keberhasilan (fulfilled) dan callback untuk menangani kegagalan (rejected).

Lalu, apa itu Promise? Mungkin, Anda penasaran juga isi dari doSomething. Nah, berikut isinya.

  1. fungsi promiseExecutor(resolve, reject) {
  2.   setTimeout(() => {
  3.     console.log('Melakukan sesuatu sebelum Janji terselesaikan.');
  4.     // Penentuan hasil dari proses asinkron
  5.     konstanta angka = Math.random();
  6.     // Nilai pemenuhan dari Janji
  7.     jika (angka > 0,5) {
  8.       selesaikan('Kamu berhasil!');
  9.     }
  10.     // Nilai penolakan dari Promise
  11.     kalau tidak {
  12.       reject(new Error('Maaf, terjadi kesalahan!'));
  13.     }
  14.   }, 2000);
  15. }
  16. fungsi doSomething() {
  17.   kembalikan Promise baru (promiseExecutor);
  18. }

Function doSomething akan membuat dan mengembalikan nilai objek Promise. Ini ditandai dengan keyword new dan diikuti dengan objek Promise. Oleh karena itu, proses asinkron dimulai. Constructor Promise menerima satu buah callback dengan dua buah argumen. Kita namai argumen pertama adalah resolve dan argumen kedua adalah reject.

Ingat! Karena ini proses asinkron, kita harus menanganinya dengan method then untuk memasuki penanganan berikutnya, baik fulfilled maupun rejected.

Kunci dari cara kerja Promise adalah ia akan memasuki kondisi fulfilled dan mengeksekusi onFulfilled jika resolve terpanggil. Sebaliknya, jika reject yang ditemui, Promise akan memasuki kondisi rejected dan onRejected berjalan.

Silakan jalankan interactive code berikut dan lakukan berkali-kali untuk mencapai hasil yang berbeda.

Jika ada yang menebak bahwa kita masih pakai callback, yap, memang hal itu benar. Namun, Promise dapat terhindar dari callback hell dengan melakukan chaining method then.


Chaining

Kita lanjutkan lagi kasus pemesanan kopi di kafe. Di sana, kita sudah mendapati dua buah proses asinkron, yaitu membuat kopi dan mengantarkan kopi. Keduanya dilakukan oleh pramusaji dan proses berikutnya sangat bergantung terhadap proses sebelumnya. Di sinilah chaining promise dapat mengatasi masalah.

Perhatikan penerapan chaining pada kode berikut.

Pada main.mjs, pemanggilan then terjadi dua kali karena masih ada proses berikutnya, yaitu mengirimkan kopi, setelah pembuatan kopi sukses. Penanganan ini disebut dengan chaining method. Ini bisa dilakukan karena method then juga mengembalikan nilai Promise sebetulnya sehingga proses asinkron bisa kita teruskan.

Jika merasa penanganan error setiap proses asinkron sama, kita bisa memanfaatkan method catch. Ini artinya seluruh kemungkinan error yang dapat terjadi pada setiap proses akan memasuki method tersebut.

  1. makeCoffee(order)
  2. .then((value) => {
  3.     return sendCoffee(value);
  4.   })
  5. .then((value) => {
  6.     console.log(`Pramusaji memberikan ${value} pesanan.`);
  7.     console.log(`Saya mendapatkan ${value} dan menghabiskannya.`);
  8.   })
  9.   .catch((error) => {
  10.     console.log(error.message);
  11.   });

Silakan ubah kode pada interactive code di atas agar menerapkan method catch.

Berapa pun banyaknya proses asinkron yang perlu dilakukan untuk mencapai suatu hasil, kita dapat memanfaatkan method then.

  1. buatKopi(pesanan)
  2.   .then((value) => { /* Lakukan tugasmu... */ })
  3.   .then((value) => { /* Lakukan tugasmu... */ })
  4.   .then((value) => { /* Lakukan tugasmu... */ })
  5.   .then((value) => { /* Lakukan tugasmu... */ })
  6.   .then((value) => { /* Lakukan tugasmu... */ })
  7.   .then((value) => { /* Lakukan tugasmu... */ })
  8.   .catch((error) => console.log(error.message));


Common Problem with Promise

Tahukah Anda bahwa kita bisa saja terjebak kepada callback hell lagi meskipun memanfaatkan Promise sebagai penanganannya? Berikut buktinya.

  1. buatKopi(pesanan).lalu((nilai) => {
  2.   kirim Kopi(nilai).lalu((nilai) => {
  3.     console.log(`Pramusaji memberikan ${value} pesanan.`);
  4.     console.log(`Saya mendapatkan ${value} dan menghabiskannya.`);
  5.   });
  6. });

Walaupun tetap berjalan dengan baik, kode di atas akan berpotensi terjebak pada callback hell. Makin banyak proses asinkron berikutnya maka kode makin menjorok ke dalam. Jadi, silakan manfaatkan chaining method untuk mengatasi ketergantungan proses ini.

Pastikan kita mengembalikan nilai Promise-nya (return) jika memanfaatkan chaining method. Hal ini karena then akan berjalan jika menemukan objek Promise.

  1. buatKopi(pesanan)
  2.   .then((nilai) => {
  3.     sendCoffee(nilai); // <-- tidak akan dilanjutkan ke waktu berikutnya.
  4.   })
  5.   .then((nilai) => {
  6.     console.log(`Pramusaji memberikan ${value} pesanan.`);
  7.     console.log(`Saya mendapatkan ${value} dan menghabiskannya.`);
  8.   })
  9.   .catch((error) => {
  10.     console.log(error.message);
  11.     lempar kesalahan;
  12.   });

Contoh kode di atas akan berhenti sampai then pertama saja. Oleh karena itu, pastikan kembalikan nilai Promise-nya, ya.

  1. buatKopi(pesanan)
  2.   .then((nilai) => {
  3.     kembalikan sendCoffee(nilai); // <-- akan dilanjutkan ke berikutnya.
  4.   })
  5.   .then((nilai) => {
  6.     console.log(`Pramusaji memberikan ${value} pesanan.`);
  7.     console.log(`Saya mendapatkan ${value} dan menghabiskannya.`);
  8.   })
  9.   .catch((error) => {
  10.     console.log(error.message);
  11.     lempar kesalahan;
  12.   });

Mantap! Kode kita makin enak dibaca dengan adanya Promise. Anda ingin penulisan kode yang lebih enak lagi? Mari kita pelajari async-await dalam materi berikutnya.

Penanganan Bergaya Synchronous dengan Async-Await

Penanganan Promise benar-benar memudahkan programmer JavaScript. Kode menjadi makin simpel dan enak dibaca. Namun, proses asynchronous tetaplah tidak akan mencegah eksekusi kode setelahnya.

Perhatikan contoh berikut.

Misalnya Anda tetap ingin memiliki pengalaman kode yang berjalan secara sekuensial. Mari kita sebut ini sebagai gaya synchronous. Dengan harapan, berikut adalah output yang diharapkan jika kode di atas dijalankan ulang.

  1. Start.
  2. You did it.
  3. End.

Untuk mencapai gaya synchronous, JavaScript memiliki fitur async-await untuk menangani Promise. Mari kita belajar implementasinya.

Istilah “async-await” terdiri dari dua keyword JavaScript, yaitu async dan await. Kita perlu menggunakan mereka berdua secara bersamaan dan tidak bisa mencederai salah satunya. Hal yang perlu dicatat adalah fitur ini hanya bisa dipakai jika menggunakan function.

Berikut adalah hasil perubahan contoh kode di atas.

  1. impor { doSomething } dari './utils.mjs';
  2. fungsi asinkron promiseWithAsyncAwait() {
  3.   console.log('Mulai.');
  4.   const result = await doSomething();
  5.   console.log(hasil);
  6.   console.log('Selesai.');
  7. }
  8. promiseWithAsyncAwait();
  9. /* Output:
  10. Awal.
  11. Kamu berhasil.
  12. Akhir.
  13. */

Lihat! Kode dalam function benar-benar dibaca secara urut dari atas ke bawah. Data yang dikembalikan dari proses asynchronous akan dikeluarkan dari function dan dapat kita simpan dalam variabel. Ini terjadi berkat async-await. Keren!

Ada pertanyaan besar yang seharusnya muncul dalam benak kita, yaitu tentang cara penanganan error-nya jika menggunakan async-await. Ini menandakan Anda bisa berpikir kritis. Jawabannya adalah memanfaatkan try-catch. Error yang terjadi dari proses asynchronous akan langsung memasuki block catch.

Jika diterapkan, berikut hasil perubahan kodenya.

Kami pun merasa nyaman dengan adanya fitur ini. Anda juga merasakan hal yang sama, kan? Kita jadi makin percaya diri jika berhadapan dengan proses asynchronous.

Pelajaran kita belum selesai sampai di sini. Ada satu hal lagi yang perlu kita bahas dari objek Promise. Mari kita lanjut. Let’s go!

Concurrency dengan Promise

Kita sering berhadapan dengan banyak proses asynchronous sekaligus dalam kehidupan nyata. Apakah Anda sadar? Contohnya kita memiliki tiga buah tugas yang perlu diselesaikan.

  1. Mencuci pakaian kotor dengan mesin cuci.
  2. Menyapu lantai dengan robot pembersih.
  3. Membuat kopi.

Kita tahu bahwa dua tugas pertama didelegasikan ke mesin. Lalu, kita bisa mengerjakan tugas ketiga sembari dua pekerjaan tersebut dalam proses penyelesaian. Ini bisa kita sebut dengan proses concurrency karena banyak proses asynchronous berjalan secara bersamaan.

Untuk melakukan hal ini, objek Promise memiliki beberapa static method, seperti Promise.all dan Promise.allSettled. Keduanya memiliki kemiripan. Hal yang membedakan adalah Promise.all akan memasuki kondisi rejected jika salah satu Promise saja terjadi kegagalan. Namun, ini tidak berlaku dengan Promise.allSettled.

Perhatikan contoh pemanfaatan Promise.all jika salah satu Promise gagal.

Yap, kode masuk ke method catch. Ini tidak akan terjadi dengan Promise.allSettled. Semua Promise akan dikembalikan meskipun ada yang gagal.

Hal yang perlu kita tahu adalah tipe parameter static method, yaitu array berisi sejumlah Promise. Oleh karena itu, static method Promise dapat menjalankan banyak Promise.

Demikian materi mengenai concurrency dengan Promise. Jadi, kita bisa menangani proses asynchronous sekaligus. No ribet!

Rangkuman Asynchronous Process

Kita telah belajar banyak hal dalam materi Asynchronous Process. Kami siapkan rangkuman materi terhadap hal yang telah Anda pelajari selama ini. Harapannya, Anda dapat mengingat kembali setiap materi yang disampaikan.

Apa Itu Asynchronous Process

Asynchronous atau asinkron adalah sebuah operasi yang memungkinkan dijalankan oleh mesin dan kemudian dapat beralih fokus untuk menjalankan tugas-tugas (operasi) berikutnya sembari menunggu operasi sebelumnya selesai. Keuntungannya, proses ini tidak akan menimbulkan blocking terhadap proses-proses berikutnya yang sedang mengantre.

Jika digambarkan, berikut adalah kejadian sebuah program yang menunjukkan perbedaan antara synchronous dan asynchronous.


dos-8f1aad38deec23372b74f8f10abd91d320240731142933.jpeg


Simulasi Asynchronous Proses

Salah satu global function JavaScript yang dapat berjalan secara asynchronous adalah setTimeout. Function ini akan menjalankan sebuah callback function setelah timeout berakhir. Meskipun timeout ditetapkan menjadi 0, setTimeout akan tetap berjalan secara asinkron. Ini sangat cocok untuk didemonstrasikan dalam pembelajaran awal agar kita memiliki gambaran lebih cepat atas efek dari proses asynchronous.

dos-f205e245c802d6a596e1c14d11e5864720240731142935.jpeg


Penanganan dengan Callback

Proses asinkron memang akan mengubah cara kita berpikir dalam menyelesaikan masalah karena gaya penulisan akan berbeda. Nah, perbedaan ini menyebabkan penanganan hasil dari proses asinkron berbeda juga. Ada banyak cara dalam hal ini. Salah satu yang dipelajari adalah menggunakan teknik callback function.

Berikut adalah contoh penanganan asynchronous process dengan callback.


Penanganan Error dengan Callback

Proses asynchronous memiliki posibilitas hasil antara keberhasilan atau kegagalan. Kegagalan terjadi tergantung terhadap faktor apa yang menjadi penyebab. Begitu pun untuk faktor keberhasilan. Jadi, developer aplikasi JavaScript harus dapat memperhatikan dan mengantisipasi kemungkinan kegagalan atau keberhasilan pada proses asinkron. 

Berikut adalah contoh penanganan error pada asynchronous process dengan callback.


Apa Itu Promise

Callback menjadi cara yang sangat baik dalam menangani proses asinkron. Namun, ada peristiwa callback hell yang akan dialami sebagai risikonya. Makin panjang proses asinkron yang bergantung terhadap proses asinkron lain maka makin dalam dan sulit penulisan kodenya. Contohnya seperti kode berikut.

dos-0f5768c3fde34f23144a9ffd0f2a9eb620240731145531.jpeg

Promise adalah sebuah objek khusus yang akan menentukan keberhasilan atau kegagalan dari proses asynchronous. Ia akan menjadi alternatif dari callback dalam menangani proses tersebut.

Promise bekerja dengan tiga buah state atau kondisi, yaitu pending (kondisi awal), fulfilled (kondisi berhasil), dan rejected (kondisi gagal).

Penanganan dengan Promise

Penanganan proses asinkron dengan Promise sangat mudah, kok. Kita bisa memanfaatkan method dari objek Promise yang bernama then.

Esensi terhindar dari risiko callback hell jatuh kepada teknik yang bernama chaining method. Teknik ini memungkinkan untuk melanjutkan proses asinkron ke proses asinkron berikutnya dengan mudah. Sebabnya, method then juga mengembalikan nilai Promise yang membawa data sukses. Untuk data gagal, hasil akan masuk ke method catch.


Penanganan Bergaya Synchronous dengan Async-Await

Hal yang menakjubkan dari penanganan proses asinkron dengan Promise adalah bisa ditulis dalam gaya synchronous. Kuncinya bisa menggunakan fitur async/await. Istilah ini terdiri dari dua keyword JavaScript, yaitu async dan await. Kita perlu menggunakan mereka berdua secara bersamaan dan tidak bisa mencederai salah satunya. Hal yang perlu dicatat adalah fitur ini hanya bisa dipakai jika menggunakan function.

  1. import { doSomething } from './utils.mjs';
  2.  
  3. function promiseExecutor(resolve, reject) {
  4.   setTimeout(() => {
  5.     resolve('You did it!');
  6.   }, 2000);
  7. }
  8.  
  9. function doSomething() {
  10.   return new Promise(promiseExecutor);
  11. }
  12.  
  13. async function promiseWithAsyncAwait() {
  14.   console.log('Start.');
  15.  
  16.   const result = await doSomething();
  17.   console.log(result);
  18.  
  19.   console.log('End.');
  20. }
  21.  
  22. promiseWithAsyncAwait();
  23.  
  24. /* Output:
  25. Start.
  26. You did it.
  27. End.
  28. */


Concurrency dengan Promise

Kita berhasil menangani banyak tugas asinkron. Karena satu proses bergantung dengan proses lainnya, mereka harus berjalan secara sekuensial, bukan? Oleh karena itu, kita memerlukan chaining method then. Jika ingin menjalankan proses asinkron secara paralel (sekaligus), kita bisa memanfaatkan static method dari Promise.

Ada dua static method yang cocok, yaitu Promise.all dan Promise.allSettled. Keduanya memiliki kemiripan. Hal yang membedakan adalah Promise.all akan memasuki kondisi rejected jika salah satu Promise saja terjadi kegagalan. Namun, ini tidak berlaku dengan Promise.allSettled. 


Berikut adalah video yang mendukung penjelasan dari modul ini.

https://youtu.be/kGu_caRJ-hw?si=Z040IdwDdVYujiLE



Bersambung ke:

KUIS Asynchronous Process


Comments