# Membatalkan Fetch API dengan AbortController: Menjadikan Backend Hono Anda Lebih Efisien

Dalam pengembangan web modern, interaksi asinkron menggunakan `fetch` API adalah hal yang lumrah. Namun, sering kali kita menghadapi skenario di mana sebuah *request* yang sedang berjalan perlu dibatalkan—misalnya, pengguna pindah ke halaman lain, mengetik kueri pencarian baru, atau secara manual mengklik tombol "Batal".

Di sinilah `AbortController` berperan di sisi klien. Tapi, apa dampaknya bagi backend? Jika klien membatalkan *request*, haruskah server tetap bekerja?

Artikel ini akan membahas cara menggunakan `AbortController` di sisi klien dan, yang lebih penting, bagaimana backend **Hono** Anda dapat mendeteksi pembatalan ini untuk menghentikan proses yang tidak perlu dan menghemat sumber daya server.

---

### Bagian 1: Sisi Klien (Frontend) - Mengenal `AbortController`

`AbortController` adalah antarmuka (interface) bawaan di browser yang memungkinkan Anda membatalkan satu atau lebih proses web asinkron sesuai permintaan.

Cara kerjanya sederhana:

1. Buat instance `AbortController`.
    
2. Instance ini memiliki properti `.signal` (sebuah `AbortSignal`).
    
3. Berikan `signal` ini ke `fetch` API sebagai opsi.
    
4. Kapanpun Anda ingin membatalkan, panggil metode `.abort()` pada *controller*.
    

Ketika `.abort()` dipanggil, `fetch` akan segera dihentikan, dan *promise* yang dikembalikannya akan di-*reject* dengan `DOMException` yang bernama `AbortError`.

#### 💡 Contoh Kode Sisi Klien (Vanilla JS)

Mari kita buat contoh sederhana dengan tombol "Mulai" dan "Batal".

HTML

```typescript
<button id="startButton">Mulai Request (5 detik)</button>
<button id="cancelButton">Batalkan Request</button>
<pre id="log"></pre>
```

JavaScript

```typescript
const startButton = document.getElementById('startButton');
const cancelButton = document.getElementById('cancelButton');
const log = document.getElementById('log');

let controller; // Kita simpan controller di scope yang lebih luas

startButton.onclick = async () => {
  // Buat AbortController baru SETIAP kali request dimulai
  controller = new AbortController();
  const signal = controller.signal;

  log.textContent = 'Memulai request ke server Hono...';
  
  try {
    const response = await fetch('/api/data-lambat', { signal });
    const data = await response.json();
    log.textContent = `Sukses: ${JSON.stringify(data)}`;
  } catch (err) {
    if (err.name === 'AbortError') {
      log.textContent = 'Request dibatalkan oleh klien!';
    } else {
      log.textContent = `Error: ${err.message}`;
    }
  }
};

cancelButton.onclick = () => {
  if (controller) {
    controller.abort(); // Kirim sinyal batal
    log.textContent = 'Mengirim sinyal pembatalan...';
  }
};
```

Pada titik ini, sisi klien sudah selesai. Ia tidak akan lagi menunggu respons dari server. Tapi, **apakah server tahu**?

---

### Bagian 2: Sisi Server (Backend) - Mengapa Hono Peduli?

Ini adalah bagian yang krusial. Saat klien memanggil `controller.abort()`, browser akan menutup koneksi HTTP ke server.

**Backend Hono Anda, yang berjalan di runtime modern (seperti Node.js, Deno, Bun, atau Cloudflare Workers), sangat cerdas.** Ia dapat mendeteksi penutupan koneksi ini.

Runtime server akan meneruskan sinyal "abort" ini ke dalam objek `Request` yang diterima oleh Hono. Hono kemudian mengekspos sinyal ini melalui **konteks (**`c`).

Mengapa ini penting?

* **Hemat Sumber Daya:** Jika *request* klien adalah kueri database yang berat atau panggilan ke API eksternal yang lambat, Anda tidak ingin server terus memprosesnya jika hasilnya tidak akan pernah dibaca.
    
* **Mencegah Pekerjaan Sia-sia:** Dengan mendengarkan sinyal *abort*, server Hono Anda dapat segera menghentikan tugasnya, membebaskan CPU, memori, dan koneksi database.
    

---

### Bagian 3: Implementasi Backend Hono yang 'Abort-Aware'

Hono memudahkan kita untuk mengakses sinyal pembatalan ini. Sinyal tersebut tersedia di `c.req.signal`.

Mari kita buat *endpoint* `/api/data-lambat` yang disimulasikan sebagai pekerjaan berat (misalnya, 5 detik) dan kita buat agar "sadar" akan pembatalan.

#### 🔌 Contoh Kode Sisi Server (Hono)

JavaScript

```typescript
// index.js (File server Hono Anda)
import { Hono } from 'hono';

const app = new Hono();

// Fungsi helper untuk simulasi delay yang bisa dibatalkan
const skippableDelay = (ms, signal) => {
  return new Promise((resolve, reject) => {
    // Jika sinyal sudah dibatalkan bahkan sebelum kita mulai, langsung reject.
    if (signal.aborted) {
      return reject(new Error('Client aborted'));
    }

    const timeout = setTimeout(resolve, ms);

    // Ini adalah intinya:
    // Dengarkan event 'abort' pada sinyal yang diberikan Hono
    signal.addEventListener('abort', () => {
      clearTimeout(timeout); // Batalkan timeout
      console.log('SERVER: Klien membatalkan, proses dihentikan!');
      reject(new Error('Client aborted')); // Reject promise
    });
  });
};

// Endpoint API kita
app.get('/api/data-lambat', async (c) => {
  const signal = c.req.signal; // Ambil sinyal dari konteks Hono

  try {
    console.log('SERVER: Proses berat dimulai...');
    
    // Tunggu 5 detik, tapi perhatikan sinyal 'abort'
    await skippableDelay(5000, signal);

    // Jika kode sampai di sini, artinya TIDAK dibatalkan
    console.log('SERVER: Proses berat selesai.');
    return c.json({ message: 'Pekerjaan selesai!' });

  } catch (err) {
    if (err.message === 'Client aborted') {
      // Kita bisa mengembalikan status khusus (non-standar) 
      // yang menandakan klien menutup request.
      return c.body(null, 499); // 499 Client Closed Request
    }
    
    // Handle error server lainnya
    return c.json({ error: 'Internal Server Error' }, 500);
  }
});

export default app;
```

#### Apa yang terjadi di sini?

1. `c.req.signal`: Hono memberikan kita `AbortSignal` yang terhubung langsung ke koneksi HTTP klien.
    
2. `skippableDelay`: Fungsi ini mensimulasikan pekerjaan. Ia menggunakan `setTimeout` untuk menunggu, tetapi juga menambahkan `addEventListener('abort', ...)` pada `signal` tersebut.
    
3. **Jika Klien Membatalkan**:
    
    * Sisi klien memanggil `controller.abort()`.
        
    * Browser menutup koneksi.
        
    * Runtime server mendeteksi ini dan memicu *event* 'abort' pada `c.req.signal`.
        
    * *Listener* kita di server aktif, `clearTimeout` dipanggil, dan *promise* di-*reject*.
        
4. **Hemat Sumber Daya**: Blok `try...catch` di Hono menangkap *error* ini. Server berhenti memproses dan mengirim respons (yang mungkin tidak terkirim, tapi setidaknya prosesnya berhenti). Konsol server akan mencatat "SERVER: Klien membatalkan...".
    

---

### Kesimpulan

Menggunakan `AbortController` bukan hanya sekadar "trik" *frontend* untuk mempercantik UI. Ini adalah pola desain *full-stack* yang penting untuk membangun aplikasi yang benar-benar responsif dan efisien.

Dengan memanfaatkan `c.req.signal` di **Hono**, Anda dapat membuat backend yang cerdas, yang tidak membuang-buang siklus CPU untuk *request* yang sudah ditinggalkan oleh pengguna. Ini adalah praktik terbaik yang membedakan aplikasi yang tangguh dan efisien dari yang lain.
