Fungsi asinkron memungkinkan Anda menulis kode berbasis promise seolah-olah kode tersebut merupakan kode sinkron.
Fungsi asinkron diaktifkan secara default di Chrome, Edge, Firefox, dan Safari, dan fungsi ini benar-benar luar biasa. Fungsi ini memungkinkan Anda menulis kode berbasis promise seolah-olah kode tersebut sinkron, tetapi tanpa memblokir thread utama. Hal ini membuat kode asinkron Anda menjadi kurang "pintar" dan lebih mudah dibaca.
Fungsi asinkron berfungsi seperti ini:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
} catch (rejectedValue) {
// …
}
}
Jika menggunakan kata kunci async
sebelum definisi fungsi, Anda dapat menggunakan
await
dalam fungsi. Saat Anda await
promise, fungsi akan dijeda
dengan cara yang tidak memblokir hingga promise selesai. Jika promise terpenuhi, Anda akan mendapatkan nilai kembali. Jika promise ditolak, nilai yang ditolak akan ditampilkan.
Dukungan browser
Contoh: mencatat pengambilan
Misalnya, Anda ingin mengambil URL dan mencatat responsnya sebagai teks. Berikut tampilannya menggunakan promise:
function logFetch(url) {
return fetch(url)
.then((response) => response.text())
.then((text) => {
console.log(text);
})
.catch((err) => {
console.error('fetch failed', err);
});
}
Dan berikut adalah hal yang sama menggunakan fungsi asinkron:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
} catch (err) {
console.log('fetch failed', err);
}
}
Jumlah barisnya sama, tetapi semua callback hilang. Hal ini membuat kode lebih mudah dibaca, terutama bagi mereka yang kurang memahami promise.
Nilai yang ditampilkan asinkron
Fungsi asinkron selalu menampilkan promise, baik Anda menggunakan await
maupun tidak. Promise tersebut
akan diselesaikan dengan apa pun yang ditampilkan fungsi asinkron, atau ditolak dengan
apa pun yang ditampilkan fungsi asinkron. Jadi, dengan:
// wait ms milliseconds
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function hello() {
await wait(500);
return 'world';
}
…memanggil hello()
akan menampilkan promise yang memenuhi dengan "world"
.
async function foo() {
await wait(500);
throw Error('bar');
}
…memanggil foo()
akan menampilkan promise yang rejects dengan Error('bar')
.
Contoh: melakukan streaming respons
Manfaat fungsi asinkron meningkat dalam contoh yang lebih kompleks. Misalnya, Anda ingin menstreaming respons saat logout dari chunk, dan menampilkan ukuran akhir.
Berikut ini dengan promise:
function getResponseSize(url) {
return fetch(url).then((response) => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
});
});
}
Lihat saya, Jake "pemberi janji" Archibald. Lihat cara saya memanggil
processResult()
di dalamnya untuk menyiapkan loop asinkron? Menulis membuat
saya merasa sangat pintar. Namun, seperti kebanyakan kode "cerdas", Anda harus menatapnya selama
bertahun-tahun untuk mengetahui apa yang dilakukannya, seperti salah satu gambar magic-eye dari
tahun 90-an.
Mari kita coba lagi dengan fungsi asinkron:
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
Semua "smart" telah hilang. Loop asinkron yang membuat saya merasa begitu puas diganti dengan loop while yang tepercaya dan membosankan. Jauh lebih baik. Di masa mendatang, Anda akan mendapatkan
iterator asinkron,
yang akan
mengganti loop while
dengan loop for-of, sehingga membuatnya lebih rapi.
Sintaksis fungsi asinkron lainnya
Kami telah menunjukkan async function() {}
kepada Anda, tetapi kata kunci async
dapat
digunakan dengan sintaksis fungsi lainnya:
Fungsi panah
// map some URLs to json-promises
const jsonPromises = urls.map(async (url) => {
const response = await fetch(url);
return response.json();
});
Metode objek
const storage = {
async getAvatar(name) {
const cache = await caches.open('avatars');
return cache.match(`/avatars/${name}.jpg`);
}
};
storage.getAvatar('jaffathecake').then(…);
Metode class
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
Hati-hati! Hindari terlalu berurutan
Meskipun Anda menulis kode yang terlihat sinkron, pastikan Anda tidak melewatkan kesempatan untuk melakukan hal-hal secara paralel.
async function series() {
await wait(500); // Wait 500ms…
await wait(500); // …then wait another 500ms.
return 'done!';
}
Proses di atas memerlukan waktu 1.000 md untuk diselesaikan, sedangkan:
async function parallel() {
const wait1 = wait(500); // Start a 500ms timer asynchronously…
const wait2 = wait(500); // …meaning this timer happens in parallel.
await Promise.all([wait1, wait2]); // Wait for both timers in parallel.
return 'done!';
}
Proses di atas memerlukan waktu 500 md untuk diselesaikan, karena kedua waktu tunggu terjadi secara bersamaan. Mari kita lihat contoh praktisnya.
Contoh: menampilkan pengambilan secara berurutan
Misalnya, Anda ingin mengambil serangkaian URL dan mencatatnya sesegera mungkin, dalam urutan yang benar.
Tarik napas dalam-dalam - berikut tampilannya dengan promise:
function markHandled(promise) {
promise.catch(() => {});
return promise;
}
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map((url) => {
return markHandled(fetch(url).then((response) => response.text()));
});
// log them in order
return textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise).then((text) => console.log(text));
}, Promise.resolve());
}
Ya, benar, saya menggunakan reduce
untuk membuat rantai urutan promise. Saya sangat
pintar. Namun, ini adalah kode yang sangat cerdas yang sebaiknya tidak Anda gunakan.
Namun, saat mengonversi kode di atas menjadi fungsi asinkron, Anda mungkin tergoda untuk melakukannya terlalu berurutan:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
reduce
"cerdas" diganti dengan loop for standar yang membosankan dan dapat dibaca.
Solusi dukungan browser: generator
Jika menargetkan browser yang mendukung generator (yang mencakup versi terbaru dari setiap browser utama ), Anda dapat mem-polyfill fungsi asinkron.
Babel akan melakukannya untuk Anda, berikut contoh melalui Babel REPL
- perhatikan kemiripan kode yang ditranspil. Transformasi ini adalah bagian dari preset es2017 Babel.
Sebaiknya gunakan pendekatan transpilasi, karena Anda dapat menonaktifkannya setelah browser target mendukung fungsi asinkron, tetapi jika Anda benar-benar tidak ingin menggunakan transpiler, Anda dapat menggunakan polyfill Babel dan menggunakannya sendiri. Daripada:
async function slowEcho(val) {
await wait(1000);
return val;
}
…Anda akan menyertakan polyfill dan menulis:
const slowEcho = createAsyncFunction(function* (val) {
yield wait(1000);
return val;
});
Perhatikan bahwa Anda harus meneruskan generator (function*
) ke createAsyncFunction
,
dan menggunakan yield
, bukan await
. Selain itu, cara kerjanya sama.
Solusi: regenerator
Jika Anda menargetkan browser lama, Babel juga dapat mentranspile generator, sehingga Anda dapat menggunakan fungsi asinkron hingga IE8. Untuk melakukannya, Anda memerlukan preset es2017 Babel dan preset es2015.
Outputnya tidak terlalu bagus, jadi waspadai pembesaran kode.
Semuanya asinkron.
Setelah fungsi asinkron tersedia di semua browser, gunakan fungsi tersebut di setiap fungsi yang menampilkan promise. Fungsi ini tidak hanya membuat kode Anda lebih rapi, tetapi juga memastikan bahwa fungsi tersebut akan selalu menampilkan promise.
Saya sangat antusias dengan fungsi asinkron pada tahun 2014, dan sangat senang melihatnya diluncurkan, benar-benar, di browser. Wah!