Akhir bulan, Siti harus memproses 500 laporan penjualan: setiap laporan dimuat, diringkas, lalu diarsipkan. Cara paling cepat di atas kertas adalah Promise.all atas 500 promise sekaligus — tetapi cara itu membuka 500 pekerjaan bersamaan: memori membengkak dan server tujuan menolak karena dibanjiri permintaan. Kecepatan maksimal bukan selalu keputusan terbaik; yang kita butuhkan adalah cara mengatur seberapa banyak pekerjaan berjalan sekaligus (concurrency).
Chapter ini membangun dua pola standar dari alat yang sudah kita kuasai: batch dengan Promise.all, dan antrean berurutan. Keduanya diuji nyata dengan keluaran yang deterministik.
Pola 1: Batch dengan Promise.all #
Idenya: pecah daftar tugas menjadi batch berukuran tetap — misalnya dua tugas per batch — proses satu batch penuh dengan Promise.all, tunggu selesai, baru buka batch berikutnya. Paralelisme maksimalnya selalu sama dengan ukuran batch.
Berkas contoh memakai top-level await (berkas .mjs, sama seperti contoh module di section Modules) agar alurnya lurus dari atas ke bawah:
// pool-batch.mjs
// Membatasi paralelisme: tugas diproses per batch dengan Promise.all.
function muatBerkas(nama, durasi) {
return new Promise((resolve) => {
setTimeout(() => resolve(nama), durasi);
});
}
// --- 1. membagi daftar tugas menjadi batch berukuran tetap ---
function bagiBatch(daftar, ukuran) {
const hasil = [];
for (let i = 0; i < daftar.length; i += ukuran) {
hasil.push(daftar.slice(i, i + ukuran));
}
return hasil;
}
// --- 2. memproses batch demi batch ---
const daftarBerkas = [
{ nama: "laporan-q1.csv", durasi: 30 },
{ nama: "laporan-q2.csv", durasi: 20 },
{ nama: "laporan-q3.csv", durasi: 10 },
{ nama: "laporan-q4.csv", durasi: 20 },
{ nama: "ringkasan.csv", durasi: 40 },
];
let nomorBatch = 0;
for (const batch of bagiBatch(daftarBerkas, 2)) {
nomorBatch += 1;
console.log(`--- batch ${nomorBatch} (maksimal 2 berkas) ---`);
const hasil = await Promise.all(
batch.map(({ nama, durasi }) =>
muatBerkas(nama, durasi).then((selesai) => {
console.log(" berkas selesai dimuat:", selesai);
return selesai;
})
)
);
console.log(" hasil batch (urutan input):", hasil.join(", "));
}
console.log(`semua ${daftarBerkas.length} berkas selesai diproses`);Output yang Dihasilkan:
--- batch 1 (maksimal 2 berkas) ---
berkas selesai dimuat: laporan-q2.csv
berkas selesai dimuat: laporan-q1.csv
hasil batch (urutan input): laporan-q1.csv, laporan-q2.csv
--- batch 2 (maksimal 2 berkas) ---
berkas selesai dimuat: laporan-q3.csv
berkas selesai dimuat: laporan-q4.csv
hasil batch (urutan input): laporan-q3.csv, laporan-q4.csv
--- batch 3 (maksimal 2 berkas) ---
berkas selesai dimuat: ringkasan.csv
hasil batch (urutan input): ringkasan.csv
semua 5 berkas selesai diprosesMari kita telaah baris-baris pentingnya:
- Baris 11–17 (
bagiBatch): memotong array denganslicesepanjangukuran, menghasilkan array berisi array. Daftar lima berkas menjadi tiga batch. - Baris 29–31 (loop batch):
for-ofmenyusuri batch satu per satu;nomorBatchdihitung manual agar label keluaran pasti. - Baris 33–40 (
Promise.allper batch): maksimal dua muatan berjalan serentak. Destructuring parameter({ nama, durasi })langsung membongkar objek tugas. - Baris 35–38 (
thendi dalammap): mencatat waktu selesai nyata. Pada batch 1,laporan-q2.csv(20 ms) tercatat sebelumlaporan-q1.csv(30 ms) — urutan ini dipastikan oleh durasi yang kita tentukan sendiri, bukan perlombaan yang hasilnya bisa berubah-ubah. - Baris 42 (hasil
Promise.all): selalu mengikuti urutan input —laporan-q1.csvsebelumlaporan-q2.csv— meski q2 selesai lebih dulu. Inilah yang membuatPromise.allaman untuk menyusun kembali hasil sesuai daftar awal. - Baris 45 (log akhir): tercetak setelah loop selesai;
awaitdi dalamfor-ofmenjamin batch berikutnya baru dibuka setelah batch sebelumnya tuntas.
Total waktu pola batch kira-kira menjumlahkan batch terlama dari tiap kelompok — pada contoh sekitar 30 + 20 + 40 milidetik. Angka pastinya bervariasi antar mesin, tetapi strukturnya pasti: maksimal dua muatan dalam satu waktu.
Pola 2: Antrean Berurutan #
Ketika tugas wajib satu demi satu — printer yang tidak boleh menerima dua pekerjaan bersamaan, atau API dengan kuota ketat — kita buang paralelisme sama sekali. Bentuknya antrean: array pekerjaan dikerjakan satu per satu lewat shift() sambil di-await:
function tunda(ms, nilai) {
return new Promise((resolve) => setTimeout(() => resolve(nilai), ms));
}
async function jalankanAntrean(daftarTugas) {
const antrean = [...daftarTugas];
while (antrean.length > 0) {
const tugasKini = antrean.shift();
const hasil = await tugasKini();
console.log("selesai:", hasil);
}
}
jalankanAntrean([
() => tunda(20, "cetak struk #1"),
() => tunda(10, "cetak struk #2"),
() => tunda(30, "cetak struk #3"),
]).then(() => console.log("antrean kosong, printer beristirahat"));Output yang Dihasilkan:
selesai: cetak struk #1
selesai: cetak struk #2
selesai: cetak struk #3
antrean kosong, printer beristirahatwhile dengan shift() mengosongkan antrean dari depan, dan await menjamin tugas berikutnya tidak dimulai sebelum yang berjalan selesai. Total waktunya adalah jumlah seluruh durasi — 20 + 10 + 30 milidetik pada contoh.
Satu detail penting: antrean menampung function pembuat promise, bukan promise yang sudah jadi. Pekerjaan baru hanya dibuat tepat saat gilirannya tiba. Bila kita mendaftarkan 500 promise sekaligus lewat Promise.all, pekerjaannya justru dimulai serentak — justru itu yang ingin kita cegah.
Memilih Pola yang Tepat #
| Pola | Paralelisme | Total waktu kira-kira | Cocok ketika |
|---|---|---|---|
| Antrean berurutan | 1 | jumlah semua durasi | tugas wajib satu demi satu |
| Batch per chunk | ukuran batch | jumlah batch terlama tiap kelompok | batasi beban server atau memori |
Promise.all penuh | seluruh tugas | durasi tugas terlama | tugas sedikit dan memang mandiri |
Section ini berangkat dari satu thread yang mudah terhalang, lalu berakhir dengan pola untuk mengatur banyak pekerjaan. Keterampilan yang sama langsung terpakai pada tahap berikutnya: mengambil data nyata dari server lewat fetch di section Network, dan merespons aksi pengguna lewat event DOM di section Browser Events — dua bentuk asinkron yang paling sering ditemui dalam pengembangan web.