Panduan Komplit Asynchronous Programming Pada Javascript
Panduan Komplit Asynchronous Programming Pada Javascript
Asynchronous
Programming pada
Javascript
Ada banyak sekali implementasi asynchronous dalam javascript seperti event, timer,
request ajax, listener, interaksi user dan masih banyak lagi. NodeJS merupakan
salah satu contoh sukses platform javascript yang sangat bergantung pada teknik
asynchronus.
Topik asynchronous memang termasuk salah satu topik yang tidak terlalu mudah untuk
dipahami, tapi ada istilah “practice makes perfect”. Semakin sering di praktekkan
semakin paham. Tapi praktek tanpa memahami konsep juga buta, terlalu banyak trial
and error dan makan waktu. Menurut saya cara ideal untuk memahami asynchronous
dengan memahami konsepnya adalah secara bertahap dan mempraktekkannya. Ingat
kuncinya adalah bertahap bukan borongan.
Synchronous vs Asynchronous
Dalam dunia programming kedua istilah ini digunakan untuk membedakan tentang cara
urutan eksekusi perintah-perintah yang ada dalam kode anda.
Synchronous adalah yang paling umum dan mudah di mengerti. Setiap perintah di
eksekusi satu persatu sesuai urutan kode yang anda tuliskan. Contoh :
1. console.log('Hello')
2. console.log('Javascript')
3. console.log('Coder')
4.
5. /*
6. Output :
7. Hello!
8. Javascipt
9. Coder
10. */
Output dari kode diatas dijamin akan sesuai urutan, karena setiap perintah harus
menunggu perintah sebelumnya selesai. Proses seperti ini disebut ‘blocking’.
Dalam dunia nyata ini mirip seperti antrian di BANK. Jika anda berada antrian nomor 4,
maka anda akan dilayani setelah antrian 1–3 sampai selesai.
Sedangkan Asynchronous hasil eksekusi atau output tidak selalu berdasarkan urutan
kode, tetapi berdasarkan waktu proses. Eksekusi dengan asynchronous tidak akan
membloking atau menunggu suatu perintah sampai selesai. Daripada
menunggu, asynchronous akan mengeksekusi perintah selanjutnya. Wait, sampai disini
mungkin tidak masuk akal, Contoh :
1. console.log('Hello');
2. setTimeout(() => { console.log('Javascript')},100) // tunda selama 100 miliseconds
3. console.log('Coder');
4.
5. /* ----------
6. Output :
7. Hello!
8. Coder
9. Javascipt
10. ------------*/
Catatan :
Pada baris ke 2 setTimeout digunakan untuk menunda eksekusi dalam satuan
milisecond dalam hal ini untuk simulasi prosess async.
Perhatikan bahwa outputnya tidak berurutan sesuai input (kode). Karena cara kerja
asynchronous adalah berdasarkan waktu proses. Jika ada salah satu eksekusi
membutuhkan proses yang agak lama, maka sembari menunggu prosess tersebut
javascript mengeksekusi perintah selanjutnya.
Mari kita lihat performancenya, Sebagai contoh ada 3 perintah dengan waktu proses
masing-masing.
Dari contoh simulasi diatas model eksekusi asynchronous lebih effisien.
Tapi ada yang menjadi pertimbangan yang disebut race condition. Race
Condition terjadi ketika ada satu perintah yang bergantung pada output eksekusi
asynchronous sebelumnya.Dengan kata lain kejar-kejaran. Contoh :
1. console.log('hello')
2. let user = requestAjax() // di eksekusi secara asynchronous
3. displayUser(user)
Dari contoh kode diatas besar kemungkinan displayUser menampilkan data kosong,
karena belum tentu output dari ekskusi requestAjax sudah selesai.
Solusi untuk problem ini yaitu menggunakan teknik callback, promise, generator atau
asyc/await yang akan kita kupas lebih dalam di sesi berikutnya.
Sampai disini mungkin timbul beberapa pertanyaan.
Apakah javascript secara default mengeksekusi perintah dengan metode
synchronous atau asynchronous ?
Jawabannya :
Kita tidak bisa membuat proses asynchronous murni. Tapi untuk membuat simulasi
iya kita bisa menggunakan fungsi setInternal dan setTimeout
Konsep Callback
Callback sebenarnya adalah function bedanya dengan function pada umumnya adalah di
cara eksekusinya. Jika function pada umumnya di eksekusi berurutan dari atas ke bawah
maka callback di eksekusi pada point tertentu, itu sebabnya di sebut callback.
Callback disebut juga dengan high-order function. Callback sebenarnya adalah function,
bedanya dengan function pada umumnya adalah di cara eksekusinya. Jika function pada
umumnya di eksekusi secara langsung sedangkan callback di eksekusi dalam function
lain melalui parameter.
1. function main(param1,param2,callBack){
2. console.log(param1, param2)
3. callBack()
4. }
5.
6. function myCallback(){
7. console.log ('hello callback')
8. }
9.
10. main(1,2,myCallback)
11.
12. /* ===================
13. Output :
14. 1 2
15. hello callback
16. */
Seperti object pada umumnya, function bisa memiliki property dan method
Event listener
1. function calculate(x,y){
2. result = x + y
3. return result
4. }
5. calculate(3,2) // 5
Kode diatas cukup sederhana yaitu untuk melakukan operasi penjumlahan. Berikut
tantangannya :
Buatlah function diatas agar bisa melakukan operasi matematika yg lain seperti
kurang, bagi, kali dan lain sebagainya.
Output dari function di atas harus bisa di format ke dalam mata uang
Dengan cara umum kita bisa menyelesaikanya dengan bantuan if atau switch untuk
menguji operatornya. Tapi ini akan membuat code lebih panjang dan kurang dinamis.
Dengan callback kita dapat membuat function diatas menjadi lebih dinamis
1. function calculate(param1,param2,callback){
2. //default operation
3. result = param1 + param2
4.
5. // callback is function ?
6. if (typeof callback == 'function'){
7. result= callback(param1,param2)
8. }
9.
10. return result
11. }
12.
13. //execute
14. a=calculate(2000,4000, function(x,y){return "$ " + (x + y) })
15. b=calculate(7000,2000, function(x,y){return "Rp " + (x * y) })
16. console.log(a) // $ 6000
17. console.log(b) // $ 14000
Jika anda pengguna jquery, pasti sudah terbiasa dengan event listener seperti ini :
1. $('#my_button').on('click', function(e) {
2. console.log('Ouhh aku di klik!');
3. })
1. function p1() {
2. console.log('p1 done')
3. }
4. function p2() {
5. console.log('p2 done')
6. }
7. function p3() {
8. console.log('p3 done')
9. }
10. p1()
11. p2()
12. p3()
13.
14. /* Output :
15. p1 done
16. p2 done
17. p3 done
18. */
Tetapi pada proses asynchronous output dari kode yang tuliskan tidak selalu berurutan.
Hasilnya tergantung yang mana yang lebih dulu selesai. Perhatikan contoh berikut :
1. function p1() {
2. console.log('p1 done')
3. }
4. function p2() {
5. //setTimeout or delay for asynchronous simulation
6. setTimeout(
7. function() {
8. console.log('p2 done')
9. },100
10. )
11. }
12. function p3() {
13. console.log('p3 done')
14. }
15. p1()
16. p2()
17. p3()
18.
19. /* Output :
20. p1 done
21. p3 done
22. p2 done
23. */
Perhatikan output dari kode diatas tidak lagi berurutan. Kerena javascript mengerjakan
mana yang lebih dulu selesai. Mungkin pada contoh diatas tidak terlalu masalah tapi
pada kasus tertentu ini menjadi problem, Contohnya kita ingin menampilkan data yang
harus di request terlebih dahulu dengan proses ajax.
Kemungkinan besar hasilnya undefined. Karena belum tentu data sudah tersedia ketika
function showResult( ) di eksekusi. Teknik callback dapat kita gunakan untuk problem
ini.
Baik sebelum membahas ajax lebih , kita akan coba memperbaiki challange asynchronus
di atas dengan memastikan output p1,p2,p3 sesuai urutan.
Solusi :
1. function p1() {
2. console.log('p1 done')
3. }
4.
5. function p2(callback) {
6. setTimeout(
7. function() {
8. console.log('p2 done')
9. callback()
10. },100
11. )
12. }
13.
14. function p3() {
15. console.log('p3 done')
16. }
17. p1()
18. p2(p3)
1. Callback pada proses ajax
Callback pada asynchronous seperti delegasi tugas. Seperti pada contoh sebelumnya
maka untuk mengelola komunikasi dengan ajax kita memerlukan callback
Problem :
1. data=requestAjax() // asynchronous process
2. showResult(data) //undefined
Solusi:
Solusinya adalah dengan membuat function showResult menjadi callback bagi function
requestAjax
Operasi file seperti read,write,rename dan delete file sangat umum dalam NodeJS.
Operasi file pada nodejs mendukung synchronous dan asynchronous.
1. const fs = require('fs');
2. var data = fs.readFileSync('hello.md')
3. console.log('Read File Done :' + data.toString());
Hasil eksekusi code diatas akan membloking keseluruhan prosess hingga proses baca file
selesai. Kelemahannya tentu aplikasi akan berhenti sampai proses tersebut selesai
ditambah lagi tidak ada manajemen error.
Cara yang paling umum utuk operasi file adalah menggunakan metode asynchonous
1. function readFileCallback(err,data){
2. if (err){
3. console.log('Error Read File :' + err);
4. }else{
5. console.log(data.toString())
6. }
7. }
8. var data = fs.readFile('hello.md',readFileCallback)
Callback Hell
Callback hell adalah istilah ketika membuat beberapa callback bercabang atau callback di
dalam callback. Sebagai contoh menggabung beberapa file ke dalam satu file.
1. var a = readFileContent("a.md");
2. var b = readFileContent("b.md");
3. var c = readFileContent("c.md");
4. writeFileContent("result.md", a + b + c);
5. console.log("we are done");
Sebenarnya dari segi output tidak ada problem, yang menjadi problem adalah
1. Code sulit dibaca, dalam kasus tertentu piramida code bisa menjadi lebih panjang
dan sulit untuk di maintenence
2. Tidak ada error handling, jika salah satu proses readFile error sulit di debug bagian
yang mana yang error.
Bagaimana Solusinya ?
Solusi pertama adalah dengan membuat kode yang lebih modular agar lebih mudah
dibaca. Tapi solusi yang lebih mudah adalah menggunakan promise yang akan di bahas
berikutnya.
Dalam dunia promise analogi di atas juga sama, ketika melakukan request asynchronous
seperti Ajax, maka ada 3 kemungkinan state :
Fulfilled ( berhasil )
Rejected ( gagal )
Lalu bagaimana implementasinya dalam javascript ? Untuk sekarang ingat saja bahwa
promise itu adalah object. Object yang merepresentasikan state diatas.
2.Callback vs Promise
Promise umumnya digunakan sebagai alternative callback. Salah satu tantangan
di callback adalah callback hell. Disebut neraka ketika ada callback didalam callback
didalam callback lagi dan di dalam callback lagi. Problemnya adalah kode sulit dibaca
dan penanganan error nya juga menjadi sulit. Disaat seperti ini maka promise menjadi
solusi.
1. janjian
2. .then((result) => { console.log(result) })
3. .then((error) => { console.log(error) })
1. fetch('https://jsonplaceholder.typicode.com/users/1')
2. .then(function (response) { // #1
3. return response.json()
4. })
5. .then(function (user) { // #2
6. console.log(user)
7. })
2. Hasil dari method Body.json() dibaca pada #2. Selain json kita juga menerima
respon dalam format plain text menggunakan method Body.text()
5. Promise Chaining
Seperti namanya promise chaining berarti promise berantai. Berikut contoh kasus untuk
menampilkan post dengan alur seperti berikut :
1. request post
4. tampilkan hasil
1. getPost
2. .then(getAuthor)
3. .then(getComment)
4. .then(showResult)
Berikut source code lengkapnya :
6.Promise.all
Promise.all sedikit lebih mudah, Cara kerjanya adalah menunggu hingga semua eksekusi
promise selesai dan menghasil output dalam bentuk array. Sebagai contoh untuk kasus
diatas menggunakan dengan Promise.all
7.Promise in Looping
Salah satu kasus menarik di promise adalah mengeksekusi beberapa promise dalam
looping sesuai urutan. Untuk memproses promise dalam looping, gunakan array.map
dan Promise.all
8. Promise.race
Berbeda cara eksekusi promise sebelumnya. Promice.race hanya menghasilkan promise
yang lebih dulu selesai. Sebagai contoh kasus mari kita ambil contoh olahraga
kegemaran kita semua yaitu balap karung.
Seharusnya jika peserta 2 terjatuh maka pemenang adalah peserta 1, tapi karena jiwa
mereka ini sangat nasionalis terpaksa balapan dihentikan.
Tapi karena kebetulan ini adalah pertandingan piala dunia balap karung, peraturan
balapnya di ubah. Pertandingan tetap dilanjutkan meskipun ada peserta yang terjatuh.
Untuk ini method promise di extend untuk mendeteksi promise yang reject.
Output :
Peserta 2 terjatuh,ahh sudahlah lanjutkan saja
Balapan selesai,Pemenangnya adalah: Peserta 1
9. Library Promise
Semua fungsi promise yang di gunakan disini menggunakan standar ES6, jika anda ingin
mengeksplor lebih dalam lagi tentang promise atau fitur promise standar belum cukup,
anda bisa mencoba library-library promises yang menawarkan fitur yang lebih banyak
seperti Bluebird,Q, RSVP,Catiline dan masih banyak lagi.
Keterangan :
async → mengubah function menjadi asynchronous
await → menunda eksekusi hingga proses asynchronous selesai, dari kode di atas
berarti console.log(result) tidak akan di eksekusi sebelum prose doAsync(
) selesai. await juga bisa digunakan berkali-kali di dalam function
Sekarang mari kita coba untuk fetch dengan promise dan async/await
Output nya sama tapi async/await lebih mudah dibaca. Lalu bagaimana dengan
manajemen error nya ?