Mengambil Data: Loading, Error & Async

Pola baku mengambil data asinkron: state data/loading/error, async/await dengan try/catch/finally, fetch, race condition, dan membungkusnya sebagai composable.

Part 23 dari 25 ± 50 menit Gratis

Hampir semua aplikasi butuh data dari server

Daftar produk, profil pengguna, cuaca — semuanya datang lewat jaringan, yang tidak instan dan bisa gagal. Tampilan yang baik harus menangani tiga keadaan: sedang memuat, berhasil, dan gagal. Mengabaikannya membuat pengguna melihat layar kosong tanpa tahu apa yang terjadi.

Pola tiga state

<script setup>
import { ref, onMounted } from 'vue'

const data = ref([])
const loading = ref(false)
const error = ref('')

async function muat() {
  loading.value = true
  error.value = ''
  try {
    const res = await fetch('https://api.contoh.com/produk')
    if (!res.ok) throw new Error('HTTP ' + res.status)   // fetch tidak otomatis melempar error utk 404/500!
    data.value = await res.json()
  } catch (e) {
    error.value = 'Gagal memuat: ' + e.message
  } finally {
    loading.value = false                                // selalu jalan, sukses maupun gagal
  }
}

onMounted(muat)
</script>

<template>
  <p v-if="loading">Memuat...</p>
  <p v-else-if="error">{{ error }} <button @click="muat">Coba lagi</button></p>
  <ul v-else>
    <li v-for="p in data" :key="p.id">{{ p.nama }}</li>
  </ul>
</template>
BagianPeran
tryKode yang mungkin gagal
catchMenangani kegagalan (isi pesan error)
finallyDijalankan selalu — tempat mematikan loading

async / await dalam sekejap

  • async function selalu mengembalikan Promise.
  • await "menunggu" Promise selesai, tanpa memblokir halaman.
  • Kegagalan Promise menjadi exception yang bisa ditangkap try/catch.

Beberapa permintaan sekaligus

const [produk, kategori] = await Promise.all([
  fetch('/api/produk').then(r => r.json()),
  fetch('/api/kategori').then(r => r.json()),
])

Mengirim data (POST)

await fetch('/api/tugas', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ judul: 'Belajar Vue' }),
})

Race condition: respons yang datang terlambat

Jika pengguna mengetik cepat, permintaan lama bisa selesai setelah permintaan baru dan menimpa hasil yang benar. Gunakan onCleanup pada watch atau AbortController:

watch(kataKunci, async (baru, _, onCleanup) => {
  const ctl = new AbortController()
  onCleanup(() => ctl.abort())                  // batalkan permintaan usang
  const res = await fetch('/api/cari?q=' + baru, { signal: ctl.signal })
  hasil.value = await res.json()
})

Bungkus jadi composable

// useFetch.js
export function useFetch(url) {
  const data = ref(null), error = ref(null), loading = ref(true)
  fetch(url).then(r => r.json()).then(d => (data.value = d))
    .catch(e => (error.value = e)).finally(() => (loading.value = false))
  return { data, error, loading }
}

Untuk kebutuhan lanjut (cache, retry, refetch otomatis) pertimbangkan pustaka seperti TanStack Query atau VueUse useFetch.

Di latihan ini kita tidak memanggil jaringan sungguhan; fungsi ambilData() mensimulasikan API dengan setTimeout agar hasilnya bisa dicek otomatis. Logika loading/error/data-nya sama persis dengan yang dipakai dengan fetch.

Kesalahan umum

  • Lupa mematikan loading saat gagal (taruh di finally).
  • Mengira fetch melempar error untuk status 404/500 — periksa res.ok.
  • Tidak mereset error sebelum permintaan baru sehingga pesan lama tetap tampil.

Praktikkan langsung, gratis

Tulis dan jalankan kode langsung di browser, dapatkan penilaian otomatis. Daftar gratis kapan saja untuk menyimpan progres dan meraih sertifikat.

Coba latihan sekarang, tanpa daftar
Bagikan: WhatsApp Facebook LinkedIn X