Setiap method asinkron harus menyatakan return type yang menggambarkan bentuk hasilnya. Pilihan yang tersedia bukan hanya Task dan Task<T>; ada pula ValueTask<T> untuk jalur panas yang sering mengembalikan hasil dari cache, serta void yang hanya sah untuk event handler. Memilih return type yang tepat membuat API asinkron jelas bagi pemanggilnya dan menghindari pemborosan alokasi.
Ringkasan Pilihan Return Type #
| Return type | Kegunaan | Catatan |
|---|---|---|
Task | Pekerjaan tanpa nilai kembali | Pasangan await tanpa hasil |
Task<T> | Pekerjaan dengan satu nilai T | Pilihan utama untuk sebagian besar kasus |
ValueTask<T> | Hasil sering sudah tersedia sinkron | Menghemat alokasi task pada jalur panas |
void | Event handler asinkron saja | Exception tidak dapat ditangkap pemanggil |
ValueTask<T> membungkus hasil atau Task<T> dalam satu struct, sehingga kasus dengan hasil cache tidak perlu mengalokasikan objek task sama sekali.
Contoh: Tiga Bentuk Hasil Asinkron #
Kode berikut memperlihatkan Task, Task<string>, dan ValueTask<int> yang berperilaku berbeda sesuai sumber datanya:
static async Task SimpanLogAsync(string pesan)
{
await Task.Delay(30);
Console.WriteLine($"Log tersimpan: {pesan}");
}
static async Task<string> AmbilSapaanAsync(string nama)
{
await Task.Delay(30);
return $"Halo, {nama}!";
}
static int _stokCache = 42;
static ValueTask<int> AmbilStokAsync(bool perluCekGudang)
{
if (!perluCekGudang)
{
return new ValueTask<int>(_stokCache);
}
return new ValueTask<int>(CekGudangAsync());
}
// Pemanggil:
await SimpanLogAsync("Aplikasi kasir dimulai");
string sapaan = await AmbilSapaanAsync("Budi");
Console.WriteLine(sapaan);
ValueTask<int> stok = AmbilStokAsync(perluCekGudang: false);
Console.WriteLine($"Stok dari cache: {await stok}");
stok = AmbilStokAsync(perluCekGudang: true);
Console.WriteLine($"Stok dari gudang: {await stok}");Output yang Dihasilkan:
Log tersimpan: Aplikasi kasir dimulai
Halo, Budi!
Stok dari cache: 42
Stok dari gudang: 37Mari kita bedah tiga bentuknya:
await SimpanLogAsync(...): pemanggil hanya menunggu pekerjaan selesai; tidak ada nilai yang perlu ditampung.string sapaan = await AmbilSapaanAsync(...): hasilawaitbertipestringsesuaiTpadaTask<string>.return new ValueTask<int>(_stokCache): jalur cache mengembalikan nilai secara langsung tanpa membuatTask<int>, sehingga tidak ada alokasi task.return new ValueTask<int>(CekGudangAsync()): jalur gudang membungkusTask<int>biasa, karena pekerjaan memang belum selesai saat pemanggilan.
Bab berikutnya membahas cara merangkai beberapa task menjadi satu alur kerja yang utuh.