Javascript Promise Combinators Concurrency Patterns Parallel Requests
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama JavaScript untuk melanjutkan topik lainnya.
Kembali ke rujukan utama JavaScript untuk melanjutkan topik lainnya.
Promise combinators adalah helper bawaan JavaScript untuk menggabungkan beberapa Promise dan menentukan apa yang harus dilakukan ketika hasilnya selesai, gagal, atau sebagian berhasil. Yang paling sering dipakai adalah Promise.all(), Promise.allSettled(), Promise.race(), dan Promise.any().
Di luar combinators, ada juga pola concurrency yang sering muncul saat kerja harian:
Topik ini sering kelihatan sederhana, tapi di praktiknya justru jadi sumber bug yang paling sering: loading state salah, error handling kepotong, request kebanyakan, atau hasil pertama yang datang bukan hasil yang benar-benar kita butuhkan.
Pakai combinators dan concurrency patterns saat kamu berurusan dengan banyak Promise sekaligus, misalnya:
Kalau hanya ada satu request, combinator biasanya tidak perlu. Begitu ada dua atau lebih Promise dengan relasi hasil yang berbeda, pilihan combinator mulai penting.
| Kebutuhan | Pilihan |
|---|---|
| Semua Promise harus sukses, kalau satu gagal langsung gagal | Promise.all() |
| Ingin tahu semua hasil, sukses dan gagal, tanpa short-circuit | Promise.allSettled() |
| Ingin hasil pertama yang selesai, apa pun statusnya | Promise.race() |
| Ingin hasil sukses pertama, abaikan yang gagal sampai semuanya gagal | Promise.any() |
| Operasi harus berurutan | sequencing dengan await satu per satu |
| Operasi independen dan ingin cepat | parallel requests dengan Promise.all() |
Semua combinator menerima iterable berisi Promise atau nilai yang bisa diubah menjadi Promise. Hasil akhirnya tetap sebuah Promise baru.
Hal pentingnya:
race() dan any() yang hanya peduli hasil pertamaPromise.all()Promise.all() dipakai saat semua hasil dibutuhkan. Kalau satu Promise gagal, seluruh hasil langsung ditolak.
Pakai Promise.all() saat semua dependency memang wajib ada.
Contoh:
const [user, posts, notifications] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r
Kalau salah satu request gagal, await di atas melempar error dan tiga hasil itu tidak akan tersedia sebagai array sukses.
Promise.all() mengembalikan hasil sesuai urutan selesai; yang benar sesuai urutan inputPromise.all() untuk data yang sebenarnya boleh sebagian suksesPromise.allSettled()Promise.allSettled() dipakai saat kamu ingin tahu semua hasil, apa pun statusnya.
{ status: 'fulfilled', value: ... }
{ status: 'rejected', reason: ... }Pakai ini saat kegagalan parsial masih bisa diterima.
Contoh:
const results = await Promise.allSettled([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json
allSettled() otomatis memberi nilai sukses langsung; tetap perlu cek statusreason untuk item gagalallSettled() padahal satu kegagalan harus menghentikan alurPromise.race()Promise.race() selesai begitu Promise pertama settle: bisa resolved atau rejected.
Pakai race() saat yang penting adalah hasil paling cepat.
Contoh:
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), ms);
});
}
const response =
race() akan menunggu hasil sukses pertama; tidak, reject pertama juga menangrace() untuk timeout tanpa abort request yang sebenarnya masih berjalanKalau kamu benar-benar ingin menghentikan request fetch yang kalah, gunakan AbortController, bukan race() saja.
Promise.any()Promise.any() resolve saat Promise sukses pertama muncul. Ia mengabaikan reject sampai semua Promise gagal.
AggregateErrorPakai any() saat kamu butuh satu jawaban sukses dari beberapa kandidat.
Contoh:
const data = await Promise.any([
fetch('https://api-a.example.com/data').then(r => r.json()),
fetch('https://api-b.example.com/data').then(r => r.json()),
fetch
any() sama dengan race(); bedanya besar: any() mengabaikan reject awalAggregateError saat semua kandidat gagalSequencing dipakai saat urutan eksekusi penting, atau request berikutnya bergantung pada hasil request sebelumnya.
const user = await fetch('/api/user').then(r => r.json());
const profile = await fetch(`/api/profile/${user.id}`).then(r => r.
Di sini request kedua dan ketiga tidak bisa dijalankan sebelum user.id tersedia.
await berurutan untuk request yang sebenarnya independenParallel requests dipakai saat beberapa request tidak saling bergantung dan bisa dikerjakan bersamaan.
const userPromise = fetch('/api/user').then(r => r.json());
const postsPromise = fetch('/api/posts').then(r => r.json());
const statsPromise =
Karena request dikirim bersamaan, total waktu biasanya mendekati request paling lambat, bukan penjumlahan semuanya.
Kadang kita ingin tetap paralel, tapi tidak semuanya sekaligus. Ini sering dipakai saat ada banyak item, misalnya 100 URL, 1.000 record, atau job sinkronisasi besar.
async function processBatch(items, limit = 5) {
const queue = [...items];
const running = [];
async function runNext() {
if (queue.
Pola produksi biasanya lebih rapi kalau memakai helper queue atau library concurrency limiter, tapi inti konsepnya sama: ada paralelisme, ada batasnya.
Kalau semua widget wajib ada, pakai Promise.all().
Kalau widget independen dan lebih baik menampilkan sebagian data daripada blank total, pakai Promise.allSettled().
Kalau ID detail baru diketahui setelah request pertama selesai, gunakan sequencing.
Kalau satu endpoint cukup dan kamu ingin yang berhasil paling cepat, gunakan Promise.any().
Kalau yang kamu kejar adalah batas waktu, Promise.race() bisa dipakai, tapi jangan lupa request asli tetap perlu dibatalkan dengan AbortController kalau memang mau berhenti total.
Masalah paling umum bukan syntax, tapi semantik.
all() untuk data yang boleh sebagian gagal = terlalu kerasallSettled() untuk data yang wajib lengkap = terlalu lunakrace() dipakai padahal butuh sukses pertama = salah perilakuany() dipakai padahal semua reject harus dilaporkan = informasi penting hilangPromise.all() mengembalikan hasil sesuai urutan input. Itu sering disalahpahami saat beberapa request selesai tidak berurutan.
Mengirim terlalu banyak request sekaligus bisa bikin:
race() dan any() hanya memilih hasil. Mereka tidak otomatis menghentikan Promise lain.
Kalau sumber daya penting, tambahkan cancellation dengan AbortController atau mekanisme pembatalan lain.
Error dari allSettled() dan AggregateError dari any() punya bentuk berbeda. Kalau parser error handling terlalu sederhana, detail penting bisa hilang.
await di dalam loop tanpa sengajafor (const id of ids) {
const item = await fetch(`/api/item/${id}`).then(r => r.json());
items.push(item);
}Kode di atas valid, tapi berjalan sequential. Kalau id independen dan jumlahnya banyak, ini sering jadi sumber lambat yang tidak disadari.
Promise.all() kalau semua hasil wajib suksesPromise.allSettled() kalau semua outcome perlu dibacaPromise.race() kalau yang penting hasil pertama settlePromise.any() kalau yang penting sukses pertamaEvent Loop, Promise, Microtask, dan async/awaitFetch API, Async Data Flow, JSON Parsing, dan Error Handling DasarError Handling di JavaScript: try/catch, throw, Custom Error, dan Debugging Mindset