Dart Asynchronous

Secara harfiah, asynchronous berarti “tidak terjadi pada waktu yang sama”. Dalam pemrograman, ini adalah teknik untuk menjalankan operasi yang memakan waktu lama tanpa memblokir eksekusi program. Ini sangat penting untuk menjaga aplikasi tetap responsif, terutama saat melakukan operasi input/output (I/O) seperti:

  • Mengambil data dari internet (API).
  • Membaca atau menulis file dari disk.
  • Terhubung ke database.

Pada Dart, hasil dari operasi asinkron direpresentasikan oleh sebuah objek Future. Sebuah Future adalah janji bahwa suatu nilai (atau error) akan tersedia di masa depan. Future memiliki tiga status:

  • Uncompleted: Awalnya, operasi belum selesai.
  • Completed with a value: Operasi selesai dengan sukses dan mengembalikan nilai.
  • Completed with an error: Operasi gagal.

Mendeklarasikan dan Menggunakan Future

Anda dapat menggunakan Future untuk menyimulasikan operasi yang memakan waktu.

// Ini adalah Future yang akan selesai dalam 2 detik
Future<String> ambilDataDariServer() {
  return Future.delayed(Duration(seconds: 2), () {
    return 'Data berhasil diambil!';
  });
}

Untuk berinteraksi dengan Future, ada dua cara utama:

1. Menggunakan .then(), .catchError(), .whenComplete()

Ini adalah pendekatan lama yang berbasis callback.

  • .then(): Akan dieksekusi ketika Future selesai dengan sukses.
  • .catchError(): Akan dieksekusi jika terjadi error.
  • .whenComplete(): Selalu dieksekusi, baik sukses maupun gagal.
ambilDataDariServer().then((data) {
  print(data); // Output: Data berhasil diambil!
}).catchError((error) {
  print('Terjadi kesalahan: $error');
}).whenComplete(() {
  print('Proses pengambilan data selesai.');
});

print('Menunggu data...'); // Ini akan dicetak terlebih dulu

Dalam contoh di atas, “Menunggu data…” dicetak terlebih dahulu karena pemanggilan ambilDataDariServer() bersifat non-blocking.

2. async dan await

Cara modern dan lebih disarankan untuk menangani Future adalah dengan menggunakan kata kunci async dan await. Ini membuat kode asinkron terlihat dan terasa seperti kode sinkron yang berurutan.

  • async: Digunakan di depan deklarasi fungsi untuk menandakan bahwa fungsi tersebut akan melakukan operasi asinkron dan mengembalikan sebuah Future.
  • await: Digunakan untuk “menunggu” selesainya sebuah Future. Kata kunci ini hanya bisa digunakan di dalam fungsi yang ditandai dengan async.

Contoh penggunaan async dan await

Future<void> prosesData() async {
  print('Memulai proses...');
  try {
    // Menunggu hingga future selesai, lalu simpan hasilnya di variabel
    String data = await ambilDataDariServer();
    print(data); // Output: Data berhasil diambil!
  } catch (e) {
    print('Terjadi kesalahan: $e');
  } finally {
    print('Proses selesai.');
  }
}

void main() {
  prosesData();
  print('Fungsi prosesData() dipanggil.'); // Ini akan dicetak duluan
}

Meskipun kode di dalam prosesData() terlihat berurutan, eksekusi main akan terus berlanjut. prosesData() akan “berhenti” pada baris await dan akan melanjutkan eksekusinya setelah ambilDataDariServer() selesai.

3. async* dan Stream

Untuk operasi yang menghasilkan serangkaian nilai dari waktu ke waktu (seperti streaming data dari internet atau event dari UI), Dart menggunakan Stream.

  • Stream: Representasi dari serangkaian data asinkron.
  • async*: Digunakan pada fungsi untuk mengembalikan sebuah Stream. Di dalam fungsi async*, Anda bisa menggunakan kata kunci yield untuk mengirimkan nilai.

Contoh penggunaan Stream dan async*

Stream<int> hitungMundur(int dari) async* {
  for (int i = dari; i >= 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i; // Mengirimkan nilai i ke stream
  }
}

void main() {
  final stream = hitungMundur(5);

  stream.listen((angka) {
    print('Hitungan: $angka');
  });

  print('Memulai hitungan mundur...');
}

Dalam contoh ini, main akan terus berjalan. stream.listen() akan menerima nilai satu per satu saat hitungMundur() mengirimkannya dengan yield.

Sample source code bisa di download di github pada link berikutĀ dart-tutorial

hyvercode