Mayoritas kode JavaScript yang pernah kita tulis bersifat synchronous, artinya setiap baris kode dieksekusi secara berurutan setelah baris sebelumnya selesai. Tapi nggak jarang juga kita nulis kode yang bersifat asynchronous, yaitu function yang nggak langsung selesai & selama function ini belum selesai, JavaScript engine bisa jalanin kode kita yang lain. Salah satu contoh kode asinkron yang paling sering kita tulis adalah network request, baik pake XMLHTTPRequest
atau, kalo pake JQuery, ya $.ajax()
.
Contoh paling sederhananya begini,
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
Kode di atas semuanya bersifat sinkron, satu baris dijalanin sesudah baris sebelumnya selesai. Outputnya begini,
1
2
3
4
5
Kode yang asinkron contohnya seperti di bawah ini,
console.log(1);
console.log(2);
setTimeout(()=>{
console.log(3, 'asinkron'); // asinkron
},500);
console.log(4);
console.log(5);
Outputnya begini, keliatan bedanya kan? Kode yang asinkron baru selesai setelah baris terakhir dijalanin.
1
2
4
5
3 'asinkron'
Callback
Cara penulisan kode asinkron yang paling sederhana adalah pake callback. Seperti contoh setTimeout
di atas. Contoh callback yang lain bisa kita tulis juga dalam bentuk event-listener misalnya document.addEventListener("DOMContentLoaded", listenerFn)
.
Promise
ES6 memperkenalkan cara “baru” untuk menulis kode asinkron, pake objek yang namanya Promise
. Kelebihan Promise
dibanding callback biasa salah satunya adalah adanya mekanisme untuk menghandel error, kondisi dimana proses gagal & nggak bisa selesai. Promise
tetap pake callback tapi dengan cara yang lebih elegan dan terstandarisasi. Sintaks dasarnya seperti di bawah,
const promise = new Promise((resolve,reject)=>{
// kalo proses berhasil, panggil resolve()
// kalo proses gagal, panggil reject()
});
Kode yang menghandel hasilnya harus kasih callback lewat then()
dan catch()
. Kalo Promise
berhasil, yang dijalanin adalah callback yang dikirim lewat then()
, sebaliknya kalo Promise
gagal, yang dijalanin adalah callback yang dikirim lewat catch()
promise
.then(()=>{}) // promise berhasil (resolve)
.catch(()=>{}) // promise gagal (reject)
Untuk contohnya, coba tulis kode berikut & biar ga usah repot buka browser jalanin pake node
aja. Jadi silakan bikin skrip JS, terus di terminal jalanin node namaskrip.js
.
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
const num = Math.round(Math.random() * 100);
if(num % 2 === 0){
resolve(num);
}else{
reject(num);
}
}, 100);
})
promise
.then((num)=>{
console.log('ok', num);
}).catch((num)=>{
console.error('not ok', num);
});
Dalam contoh kode di atas, Promise
kita anggap sukses (resolve) kalo konstanta num
bernilai genap setelah 0,1 detik. Karena kita pake Math.random()
, Promise
nggak selalu resolve, kadang reject juga.
Library JS yang secara internal ngejalanin proses asinkron semuanya mengirim objek
Promise
sebagai nilai baliknya. Salah satu contohnyafetch
. Kita nggak perlu bikinPromise
sendiri tapi tinggal pasang callback dithen()
&catch()
.
Satu Promise
bisa dikasih lebih dari satu then
& catch
.
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
const num = Math.round(Math.random() * 100);
if(num % 2 === 0){
resolve(num);
}else{
reject(num);
}
}, 100);
})
// then..catch pertama
promise
.then((num)=>{
console.log('ok', num);
}).catch((num)=>{
console.error('not ok', num);
});
// then..catch kedua
promise
.then( num=>{
console.log('ok ok',num)
}).catch( num => { console.error('not ok ok', num)} );
Promise Chain
Salah satu kelebihan Promise
dibanding skema callback biasa adalah kemudahan dalam membentuk rantai proses asinkron. Kita pasti sering ketemu kasus di mana kita harus jalanin beberapa proses secara berurutan, satu proses setelah proses sebelumnya selesai. Kalo pake callback biasa, kita pasti terpaksa nulis kode yang kadang disebut callback-hell seperti di bawah,
const proses1 = (callback, err)=>{
setTimeout(()=>{
callback('proses1 selesai');
}, 1000);
}
const proses2 = (callback, err)=>{
setTimeout(()=>{
callback('proses2 selesai');
}, 1000);
}
const proses3 = (callback, )=>{
setTimeout(()=>{
callback('proses3 selesai');
}, 1000);
}
proses1((msg1)=>{
console.log(msg1);
proses2((msg2)=>{
console.log(msg2);
proses3((msg3)=>{
console.log(msg3);
})
})
})
Baru tiga proses aja kodenya udah ruwet. Apalagi prosesnya lebih banyak & ada callback buat errornya juga. Kalo pake Promise
, kodenya bisa jadi lebih bersih & gampang dibaca. Kita tinggal me-return Promise
di setiap then()
secara berantai.
const proses1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses1 selesai');
}, 1000);
})
}
const proses2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses2 selesai');
}, 1000);
})
}
const proses3 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses3 selesai');
}, 1000);
})
}
proses1()
.then((msg)=>{
console.log(msg);
return proses2();
})
.then((msg)=>{
console.log(msg);
return proses3();
})
.then((msg)=>{
console.log(msg);
})
.catch((err)=>{
console.log('ERROR:', err);
})
Outputnya,
proses1 selesai
proses2 selesai
proses3 selesai
Menghandel error juga lebih gampang karena kalo di tengah-tengah chain ada error, proses otomatis berhenti. Jadi dalam contoh kode di atas, kalo proses2
nggak resolve, proses3
nggak akan jalan.
const proses1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses1 selesai');
}, 1000);
})
}
const proses2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
// ERROR DI SINI
reject('proses2 error');
}, 1000);
})
}
const proses3 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses3 selesai');
}, 1000);
})
}
proses1()
.then((msg)=>{
console.log(msg);
return proses2();
})
.then((msg)=>{
console.log(msg);
return proses3();
})
.then((msg)=>{
console.log(msg);
})
.catch((err)=>{
console.log('ERROR:',err);
})
Outputnya:
proses1 selesai
ERROR: proses2 error
Parallel Promise
Promise
nggak harus berurutan (chain) seperti di atas. Bisa juga dijalanin secara paralel. Kita tinggal pake Promise.all().then()
untuk jalanin proses setelah semua Promise
selesai.
const proses1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses1 selesai');
}, 1000);
})
}
const proses2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses2 selesai');
}, 1000);
})
}
const proses3 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses3 selesai');
}, 1000);
})
}
const proses4 = Promise.all([proses1(),proses2(),proses3()]);
proses4.then((messages)=>{
console.log(messages)
}).catch(err=>{
// kalo ada promise yang error
console.log('ERROR:', err);
})
[ 'proses1 selesai', 'proses2 selesai', 'proses3 selesai' ]
Coba bikin kode yang sama seperti di atas tapi pake callback biasa, pasti lebih rumit.
Promise Race
Promise.all()
bukan satu-satunya API yang bisa kita pake. Ada variasinya yaitu Promise.race()
. Bedanya kalo Promise.all()
nunggu semua Promise
selesai, Promise.race()
hanya perlu satu Promise
selesai.
const proses1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses1 selesai');
}, 3000);
})
}
const proses2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses2 selesai');
}, 1000);
})
}
const proses3 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('proses3 selesai');
}, 500);
})
}
const proses4 = Promise.race([proses1(),proses2(),proses3()]);
proses4.then((messages)=>{
console.log(messages)
}).catch(err=>{
console.log(err);
})
Karena proses3
selesai duluan, proses1
& proses2
nggak penting lagi. Jadi outputnya:
proses3 selesai
Async-Await
Ada sintaks alternatif untuk rantai promise.then()
, yang kalo terlalu panjang akhirnya juga bikin kita terjebak dalam callback-hell, yaitu sintaks async-await
.
const p1 = function() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('sukses 1');
}, 1000);
})
}
const p2 = function() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('sukses 2');
}, 1000);
})
}
// async function ini nilai baliknya adalah Promise
// yang resolve dengan nilai 'DONE'
const run = async function() {
const b = await p1()
console.log(b);
// baris dibawah baru dijalanin kalo p1 udah selesai
const c = await p2();
console.log(c);
// baris2 ini baru dijalanin kalo p2 udah selesai
console.log('selesai');
return 'DONE';
}
run().then( res => console.log(res));
Kalo ada error gimana handelnya? Ya sama seperti Promise
biasa. await ...
nantinya dikonversi oleh JS engine jadi Promise
& async function
nilai baliknya adalah Promise
juga.
const p1 = function() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
reject('error 1');
}, 1000);
})
}
const p2 = function() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
reject('error 2');
}, 1000);
})
}
// async function ini nilai baliknya adalah Promise
// yang resolve dengan nilai 'ERROR'
const run = async function() {
// error bisa dihandel pake catch
const b = await p1().catch(err => console.log(err))
// atau try-catch
try{
const c = await p2();
}catch(err){
console.log(err);
return 'ERROR';
}
console.log('selesai');
return 'DONE';
}
run().then( res => console.log(res));
O iya, Promise
nggak di-support oleh Internet Explorer. Jadi kalo aplikasi Anda harus jalan di IE, silakan pake polyfill misalnya promise-polyfill atau es6-promise.
Oke. Sekian artikelnya. Mudah-mudahan bermanfaat.
Also in this category ...
- » Flexbox
- » Mengenal Hook di ReactJS
- » Rust 101: Hello Rust
- » End-to-end Testing dengan The Intern
- » Cara Mudah Pakai Environment Variable dengan Webpack
Makasih Min Artikelnya sangat Berguna Banget Utk Bagi Pemula yang ingin mengetahui asychronus thanks ya