Belajar dengan membaca secara berurutan

Promise — Menerima Hasil Async dengan then dan catch

Terima hasil proses yang memakan waktu lewat Promise: 3 state-nya, then / catch / finally, chaining, dan Promise.all.

Ada proses yang butuh waktu sampai hasilnya datang, seperti mengirim order atau mengecek stok. Supaya halaman tidak macet selama menunggu, kode untuk proses seperti ini langsung lanjut ke baris berikutnya, lalu hasilnya diambil nanti. Cara menulis seperti ini disebut pemrosesan asynchronous.

Artikel ini membahas Promise, object yang mewakili hasil yang baru tersedia nanti, dan Promise.all, yang menunggu beberapa Promise sekaligus.

Menerima Hasilnya Nanti — Promise dan then

Misalnya jawaban soal ketersediaan kursi baru datang 100ms setelah kamu bertanya. setTimeout (function yang memanggil sebuah function satu kali setelah jumlah milidetik yang ditentukan) langsung selesai begitu dipanggil, dan eksekusi lanjut ke baris berikutnya, jadi jawaban yang baru datang 100ms kemudian tidak bisa kamu return dari function-mu.

Sebagai gantinya, kamu mengembalikan Promise (object yang mewakili hasil yang baru tersedia nanti), lalu jawabannya diantarkan lewat Promise itu. Function yang kamu berikan ke new Promise((resolve) => { ... }) langsung dipanggil saat itu juga, dan memanggil resolve(value) di dalamnya mengubah Promise dari pending (menunggu hasil) menjadi fulfilled (berhasil). Function yang kamu berikan ke method then dipanggil dengan value itu begitu Promise-nya fulfilled.

// Mengembalikan Promise yang fulfilled dengan value setelah ms milidetik
function delay(ms, value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

// Mulai mengecek (di titik ini masih pending)
const seats = delay(100, "Sisa 12 kursi");

// Berikan function yang dipanggil begitu fulfilled
seats.then((message) => {
  console.log(message);                      // Sisa 12 kursi (tampil ke-2)
});

console.log("Mengecek ketersediaan kursi");  // Mengecek ketersediaan kursi (tampil ke-1)
Function yang Diberikan ke then Jalan Paling Akhir
Panggildelay(100, …)Promise pendingdikembalikanBerikan functionke seats.then(…)Function itubelum dipanggilconsole.log dibaris berikutnyaPesan pengecekantampil duluan100ms setelahdelay dipanggilfulfilled, lalufunction-nya jalan
then hanya menyimpan function-nya; function itu tidak dipanggil saat itu juga. Function yang kamu berikan baru jalan setelah baris-baris di bawahnya selesai dan Promise-nya fulfilled.

Kalau kode yang memakai hasilnya kamu tulis di luar then, kode itu jalan sebelum hasilnya keluar, jadi value-nya belum bisa dipakai. Kumpulkan baris yang memakai hasilnya di dalam function yang kamu berikan ke then, dan taruh hal yang mau kamu kerjakan selama menunggu di baris setelahnya, di luar then.

Tambahkan Baris Penunggu di Akhir agar Output then Muncul

Code runner di halaman ini hanya mengambil output yang tampil sebelum baris terakhir selesai. Di browser atau Node.js, function then tetap jalan walaupun tidak ada yang menunggunya, tapi di halaman ini, tambahkan await delay(1000); di akhir untuk menunggu 1 detik. await (keyword yang menahan eksekusi di baris itu sampai hasil Promise keluar) dibahas di artikel berikutnya.

Tampilkan notifikasi saat masing-masing dari 2 video selesai dikonversi. delay sudah dideklarasikan.

① Di convertVideo, kembalikan Promise yang fulfilled setelah ms milidetik dengan pesan dalam bentuk "Konversi intro.mp4 selesai".

② Konversi intro.mp4 dalam 150ms dan outro.mp4 dalam 80ms, lalu tampilkan tiap pesan begitu datang.

③ Di bawah ②, tampilkan "Kamu tetap bisa mengedit selama konversi berjalan".

(Kalau berjalan dengan benar, penjelasan akan muncul.)

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Menangani Kegagalan — reject / catch / finally

Verifikasi PIN kartu bisa gagal kalau nomornya salah. Kalau yang kamu punya hanya resolve, kegagalan pun datang sebagai value biasa, sama seperti keberhasilan, jadi function penerimanya harus selalu memakai if untuk membedakan keduanya.

Function yang diberikan ke new Promise juga bisa menerima reject sebagai parameter ke-2. Kalau kamu memanggilnya, seperti reject(new Error("pesan")), Promise-nya menjadi rejected (gagal). Function yang diberikan ke catch menerima Error itu saat Promise-nya rejected, dan function yang diberikan ke finally dipanggil paling akhir, baik berhasil maupun gagal.

function verifyPin(pin) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (pin === "4821") {
        resolve("PIN cocok");
      } else {
        // Beri tahu kegagalan dengan memberikan Error ke reject
        reject(new Error("PIN salah"));
      }
    }, 100);
  });
}

// then, catch, dan finally bisa disambung berurutan (cara kerjanya dibahas di bagian berikutnya)
verifyPin("0000")
  .then((message) => console.log(message))        // tidak dipanggil, karena rejected
  .catch((error) => console.log(error.message))   // PIN salah
  .finally(() => console.log("Kolom input dikosongkan"));
  // Kolom input dikosongkan (tampil paling akhir, baik berhasil maupun gagal)
Cara resolve dan reject Menentukan State
Dibuat dengan newPromise (pending)Berikan valueke resolveBerikan Errorke rejectTidak memanggilresolve/rejectMenjadifulfilledMenjadirejectedTetappendingPanggil then,lalu finallyPanggil catch,lalu finallythen/catch/finallytidak dipanggil
Di 2 kolom kiri, finally dipanggil setelah then atau catch. Function mana yang dipanggil ditentukan oleh apakah resolve atau reject yang dipanggil.

Saat Promise rejected, function then yang disambung sebelum catch dilewati, dan function catch setelahnya yang dipanggil. catch ini adalah method Promise, berbeda dari statement try / catch. Kalau kamu lupa memanggil resolve maupun reject, kodenya akan terlihat macet tanpa output dan tanpa error.

Cek saldo gift card. delay, balances, dan codes sudah dideklarasikan.

① Di checkGiftCard, kalau kodenya tidak diawali "GIFT-", langsung buat gagal tanpa menunggu, dengan Error berisi pesan "Format kode tidak valid".

② Selain itu, buat berhasil setelah 100ms dengan pesan dalam bentuk "Saldo: 3000 yen".

③ Cek tiap kode di codes secara berurutan, lalu tampilkan pesan hasilnya.

④ Baik berhasil maupun gagal, tampilkan "Indikator pengecekan disembunyikan" di akhir.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Menjalankan Langkah Secara Berurutan — Chaining then dan return

Misalnya setelah order dibuat, kamu mau mengirim email konfirmasi memakai nomor order yang dikembalikan. Kalau request ke-2 kamu tulis di dalam then yang pertama, lalu request ke-3 di dalamnya lagi, indentasinya makin dalam di setiap langkah dan kodenya jadi susah dibaca.

then mengembalikan Promise baru, jadi kamu bisa menyambungnya seperti .then(...).then(...). Ini disebut chain (menyambungkan then berikutnya ke Promise yang dikembalikan then sebelumnya). Value yang kamu return dari function then diterima oleh function then berikutnya.

// delay sama dengan kode di bagian pertama
const placeOrder = () => delay(100, "A-2046");
const sendMail = (orderId) => delay(100, `Email konfirmasi untuk ${orderId} sudah dikirim`);

placeOrder()
  .then((orderId) => {
    console.log(`Nomor order: ${orderId}`);   // Nomor order: A-2046
    return sendMail(orderId);                // chain menunggu sampai email terkirim
  })
  .then((result) => {
    console.log(result);                     // Email konfirmasi untuk A-2046 sudah dikirim
    return "Semua langkah selesai";           // value-nya langsung diteruskan ke function berikutnya
  })
  .then((status) => console.log(status));    // Semua langkah selesai
Tanpa return, then Tidak Menunggu
returnsendMail(orderId)Menunggu sampaiemail terkirimresult berisihasil pengirimanTampil selesaisetelah terkirimHanya memanggilsendMail(orderId)then berikutnyajalan tanpa tungguresult bernilaiundefinedTampil selesaisebelum terkirim
Hanya baris atas yang menunggu email terkirim sebelum memanggil function berikutnya. then berikutnya baru menunggu hasilnya kalau kamu me-return Promise-nya.

Tanpa return, function mengembalikan undefined, jadi then berikutnya jalan tanpa menunggu email terkirim, dan result bernilai undefined. Tidak ada error yang muncul; hasil pengirimannya hilang begitu saja, sehingga kesalahan ini mudah terlewat.

Kalau Pakai Kurung Kurawal, return Tidak Boleh Dihilangkan

Arrow function tanpa kurung kurawal, seperti .then((id) => sendMail(id)), mengembalikan Promise dari sendMail apa adanya, jadi then berikutnya menunggu. Kalau kamu membungkus isinya dengan kurung kurawal untuk menambahkan baris log di tengah, tambahkan lagi return, seperti return sendMail(id);.

Bayar barang seharga 12 dolar dari toko luar negeri dalam yen. delay, dollars, loadRate, dan chargeCard sudah dideklarasikan.

① Pakai kurs untuk mengonversi dollars ke yen, tampilkan "Hasil konversi: 1800 yen", lalu teruskan jumlahnya.

② Tagih jumlah itu, dan jangan lanjut sebelum hasilnya keluar.

③ Tampilkan hasil pembayarannya.

④ Kalau ada yang gagal di tengah jalan, tampilkan "Pembayaran gagal: " diikuti pesan Error-nya.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Menunggu Beberapa Hasil Sekaligus — Promise.all

Misalnya saat dashboard admin dibuka, kamu meminta data penjualan hari ini, jumlah order, dan jumlah pertanyaan secara terpisah, lalu menampilkan panel ringkasan setelah ketiganya datang. Kalau kamu memasang then terpisah di masing-masing dari ketiga Promise itu, mana yang datang paling akhir bisa berbeda setiap kali, jadi tidak ada satu tempat pun untuk mengecek apakah semuanya sudah datang.

Kalau ketiga function itu kamu panggil lalu Promise hasilnya kamu berikan ke Promise.all (method yang menerima array Promise dan menunggu sampai semua hasilnya datang), ketiga hasil itu diterima sekaligus dalam bentuk array. Kalau ada satu saja yang rejected, alurnya masuk ke catch, bukan ke then.

// delay sama dengan kode di bagian pertama
const loadSales = () => delay(100, 184000);
const loadOrders = () => delay(50, 42);
const loadInquiries = () => delay(80, 3);

// Ketiganya mulai begitu function-nya dipanggil. Promise.all menunggu sampai semuanya datang
Promise.all([loadSales(), loadOrders(), loadInquiries()])
  // Terima array hasilnya dengan destructuring berdasarkan posisi
  .then(([sales, orders, inquiries]) => {
    // Diterima sesuai urutan di array, bukan urutan selesainya
    console.log(`Penjualan: ${sales} yen`);      // Penjualan: 184000 yen
    console.log(`Order: ${orders}`);             // Order: 42
    console.log(`Pertanyaan: ${inquiries}`);     // Pertanyaan: 3
  })
  .catch((error) => console.log(error.message));
Urutan Diberikan, Bukan Urutan Selesai
Index 0loadSales()184000 di 100msselesai ke-3Index 1loadOrders()42 di 50msselesai ke-1Index 2loadInquiries()3 di 80msselesai ke-2
Ketiganya selesai berturut-turut di 50ms, 80ms, dan 100ms. then menerima [184000, 42, 3], sesuai urutan diberikan (kolom kiri).

Promise.all sendiri tidak memulai request apa pun; method ini hanya menunggu sampai semuanya datang. Tabel di bawah menunjukkan state Promise yang dikembalikan Promise.all dan function yang dipanggil, sesuai state Promise yang diberikan.

State Promise yang diberikanPromise yang dikembalikan Promise.allYang dipanggil dan value-nya
Semua fulfilledFulfilled saat yang terakhir fulfilledthen menerima array sesuai urutan
Satu rejectedRejected saat yang satu itu rejectedcatch menerima Error tersebut
2 atau lebih rejectedRejected saat yang pertama rejectedcatch hanya menerima Error yang pertama
Lainnya fulfilled, satu pendingTetap pendingthen maupun catch tidak dipanggil

Buat estimasi gabungan untuk biaya perjalanan. 4 function yang namanya diawali quote sudah dideklarasikan.

① Di estimateTrip, tunggu sampai semua Promise yang diterima datang.

② Tampilkan total harganya sebagai "Total: 68500 yen".

③ Kalau ada satu saja yang gagal, tampilkan "Tidak bisa membuat estimasi: " diikuti pesan Error-nya.

④ Buat estimasi sekali dengan tiket pesawat, hotel, dan mobil sewaan, lalu sekali lagi dengan mobil sewaan diganti quoteFullCar, yang sudah penuh.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Kalau delay(100, "A").then((v) => console.log(v)); diikuti console.log("B");, bagaimana urutan output-nya?

Soal 2Kalau kamu menyambung .then(A).catch(B).finally(C) ke Promise yang akhirnya rejected, mana yang dipanggil?

Soal 3Array apa yang diterima then dari Promise.all([delay(100, "A"), delay(50, "B")])?