Menu

Async Await TypeScript: Tipe, Error, dan Panggilan Paralel

Cara async dan await diberi tipe di TypeScript: fungsi async mengembalikan Promise<T>, await membukanya, error ditangkap dengan try/catch, top-level await butuh ES module, serta perbedaan await satu per satu, await secara paralel, dan jebakan forEach.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Fungsi async di TypeScript selalu mengembalikan promise: kalau isinya mengembalikan string, tipenya adalah Promise<string>. Di dalamnya, await membuka Promise<T> menjadi T.

Perilaku saat runtime (apa yang dijeda oleh await, event loop) adalah urusan JavaScript, dibahas di async/await JavaScript. Bagian TypeScript adalah tipe yang masuk dan keluar.

Tipe Kembalian Fungsi Async

Tipe kembalian yang dideklarasikan untuk fungsi async harus Promise<...>, meskipun isinya mengembalikan nilai biasa. TypeScript menyimpulkannya kalau kamu tidak menuliskannya.

Menulis async function count(): number menghasilkan error TS1064 (The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'?). Utility type Awaited<T> memberikan tipe yang sudah dibuka: Awaited<ReturnType<typeof count>> adalah number.

Lupa Menulis await

await yang terlewat membuatmu memegang Promise<T>, bukan T. TypeScript menangkap sebagian besar kasus ini karena tipenya jadi tidak cocok:

Bug if (ok) ini nyata: objek promise selalu truthy, jadi akses diberikan. TypeScript melaporkannya sebagai TS2801. Memberikan promise ke variabel boolean, atau membaca property yang tidak dimiliki promise, juga akan gagal dikompilasi. Panggilan yang hasilnya kamu abaikan (save(user);) tidak tertangkap; aturan typescript-eslint no-floating-promises menangani kasus itu.

Menangani Error dengan try/catch

Promise yang di-reject membuat await melempar error, jadi try/catch biasa bisa dipakai. Dengan strict, variabel di catch bertipe unknown, dan kamu mempersempitnya sebelum membaca .message:

throw di dalam fungsi async me-reject promise-nya, bukan melempar error di tempat pemanggilan. Pola lainnya, termasuk class error kustom dan mengembalikan hasil alih-alih melempar error, ada di halaman penanganan error.

Top-Level await

await di luar fungsi apa pun hanya berjalan di ES module. File yang dikompilasi sebagai CommonJS (kasus contoh-contoh ini, dan proyek Node tanpa "type": "module") menolaknya:

index.ts(2,14): error TS1309: The current file is a CommonJS module and cannot use 'await' at the top level.

Bungkus kodenya dalam fungsi async main lalu panggil, seperti yang dilakukan setiap contoh di halaman ini. Di proyek ES module ("type": "module" di package.json dengan module diatur ke node16 atau nodenext, atau module: "esnext" untuk bundler), await di top level diizinkan.

Berurutan vs Paralel

Setiap await menunggu promise-nya sebelum baris berikutnya dimulai. Untuk panggilan yang saling independen, mulai semuanya terlebih dahulu lalu await bersama-sama dengan Promise.all:

Di bagian berurutan, b tidak bisa selesai sebelum a karena belum dimulai. Di bagian paralel, d selesai lebih dulu, dan total waktunya kira-kira sama dengan panggilan terlama, bukan jumlah semuanya. Promise.all tetap mengembalikan hasil sesuai urutan input, dengan tipe tuple.

Jebakan forEach

forEach mengabaikan promise yang dikembalikan callback async, jadi tidak ada yang menunggu pekerjaannya:

for...of dengan await memproses item satu per satu; Promise.all dengan map menjalankannya secara paralel dan menunggu semuanya. forEach tidak melakukan keduanya, dan TypeScript tidak memberi peringatan, karena callback yang bertipe mengembalikan void tetap menerima callback yang mengembalikan promise.

Iterasi Async dengan for await

for await...of melakukan perulangan pada async iterable, seperti async generator, sambil meng-await setiap nilainya:

async function* pages(total: number): AsyncGenerator<string[]> {
    for (let page = 1; page <= total; page++) {
        await new Promise((r) => setTimeout(r, 10));
        yield [`item ${page}a`, `item ${page}b`];
    }
}

async function main() {
    for await (const batch of pages(3)) {
        console.log(batch.join(", ")); // batch: string[]
    }
}
main();

Tipe elemennya berasal dari anotasi AsyncGenerator<T> pada generator, atau dari inferensi kalau kamu tidak menuliskannya.

Pertanyaan yang sering diajukan

Apa tipe kembalian fungsi async di TypeScript?

Selalu promise. Fungsi async yang mengembalikan number punya tipe kembalian Promise<number>, dan yang tidak mengembalikan apa pun bertipe Promise<void>. Menulis async function f(): number menghasilkan error TS1064, yang menyarankan Promise<number>.

Bagaimana cara memakai await di top level di TypeScript?

await di top level hanya berjalan di ES module, dengan module diatur ke es2022, esnext, system, preserve, atau node16/node18/node20/nodenext di file yang dianggap Node sebagai ESM, dan target di es2017 atau lebih tinggi. Di file CommonJS, itu menghasilkan error TS1309. Solusi yang berlaku di mana saja adalah fungsi async main: async function main() { ... } main();.

Bagaimana cara menangani error dengan async/await di TypeScript?

Bungkus await dengan try/catch. Dengan strict, nilai yang ditangkap bertipe unknown, jadi persempit dulu: if (e instanceof Error) console.log(e.message). Rejection yang tidak pernah di-await atau ditangkap menjadi unhandled rejection.

Bagaimana cara menjalankan panggilan async secara paralel di TypeScript?

Mulai semua promise terlebih dahulu, lalu await bersama-sama: const [a, b] = await Promise.all([loadA(), loadB()]). Menulis await loadA(); await loadB(); menjalankannya satu per satu. Promise.all menjaga tipe setiap hasil di tuple yang dihasilkan.

Kenapa async tidak berjalan di dalam forEach?

forEach memanggil callback dan mengabaikan nilai kembaliannya, jadi promise dari callback async tidak pernah di-await: perulangannya langsung selesai dan kode setelahnya berjalan sebelum pekerjaannya rampung. Pakai for...of dengan await untuk pekerjaan satu per satu, atau await Promise.all(items.map(async (x) => ...)) untuk pekerjaan paralel.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai