Async/Await Programming
Memahami pemrograman asynchronous di C# dengan async dan await
Async/Await Programming
Pemrograman asynchronous adalah teknik yang memungkinkan program Anda melakukan banyak operasi secara bersamaan tanpa memblokir thread utama. Ini sangat penting untuk aplikasi yang responsif.
Mengapa Async?
Masalah dengan Synchronous Code
// Synchronous - memblokir thread
public string GetDataSync()
{
// Thread menunggu selama 2 detik
var result = httpClient.GetStringAsync("https://api.example.com").Result;
return result;
}
Masalah:
- Thread tidak bisa mengerjakan hal lain
- Aplikasi βfreezeβ
- Resource terbuang sia-sia
Solusi dengan Async
// Asynchronous - thread bebas
public async Task<string> GetDataAsync()
{
// Thread bisa mengerjakan hal lain selama menunggu
var result = await httpClient.GetStringAsync("https://api.example.com");
return result;
}
Konsep Dasar
Task dan Task
// Task = operasi async tanpa return value
public async Task DoSomethingAsync()
{
await Task.Delay(1000);
Console.WriteLine("Selesai!");
}
// Task<T> = operasi async dengan return value
public async Task<int> CalculateAsync()
{
await Task.Delay(1000);
return 42;
}
async dan await Keywords
// async menandakan method adalah asynchronous
// await menunggu operasi async selesai
public async Task ProcessAsync()
{
Console.WriteLine("Mulai...");
// await = tunggu sampai selesai, tapi thread bebas
await Task.Delay(2000);
Console.WriteLine("Selesai!");
}
Pattern-Pattern Umum
Sequential Execution
// Operasi dijalankan satu per satu
public async Task SequentialAsync()
{
var result1 = await GetData1Async(); // Tunggu selesai
var result2 = await GetData2Async(); // Baru jalankan ini
var result3 = await GetData3Async(); // Lalu ini
// Total waktu = waktu1 + waktu2 + waktu3
}
Parallel Execution
// Semua operasi dijalankan bersamaan
public async Task ParallelAsync()
{
// Mulai semua task
var task1 = GetData1Async();
var task2 = GetData2Async();
var task3 = GetData3Async();
// Tunggu semua selesai
await Task.WhenAll(task1, task2, task3);
// Ambil hasil
var result1 = task1.Result;
var result2 = task2.Result;
var result3 = task3.Result;
// Total waktu = waktu terlama dari ketiganya
}
WhenAll vs WhenAny
// WhenAll - tunggu SEMUA selesai
var results = await Task.WhenAll(task1, task2, task3);
// WhenAny - tunggu SALAH SATU selesai
var firstCompleted = await Task.WhenAny(task1, task2, task3);
Async di ASP.NET Core
Controller
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _service;
[HttpGet]
public async Task<ActionResult<List<Product>>> GetAll()
{
var products = await _service.GetAllAsync();
return Ok(products);
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetById(int id)
{
var product = await _service.GetByIdAsync(id);
if (product == null)
return NotFound();
return Ok(product);
}
}
Service
public class ProductService : IProductService
{
private readonly HttpClient _httpClient;
private readonly AppDbContext _context;
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products
.FirstOrDefaultAsync(p => p.Id == id);
}
public async Task<List<Product>> GetFromExternalApiAsync()
{
var response = await _httpClient.GetAsync("https://api.example.com/products");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<List<Product>>(content);
}
}
Error Handling
Try-Catch dengan Async
public async Task<Product?> SafeGetProductAsync(int id)
{
try
{
return await _service.GetByIdAsync(id);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Gagal mengambil product {Id}", id);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error tidak terduga");
throw; // re-throw untuk error serius
}
}
Exception di Task.WhenAll
try
{
await Task.WhenAll(task1, task2, task3);
}
catch (Exception)
{
// Hanya exception pertama yang di-catch
// Untuk semua exception:
if (task1.IsFaulted) HandleError(task1.Exception);
if (task2.IsFaulted) HandleError(task2.Exception);
if (task3.IsFaulted) HandleError(task3.Exception);
}
Cancellation
CancellationToken
public async Task<Data> GetDataAsync(CancellationToken cancellationToken = default)
{
// Cek apakah di-cancel
cancellationToken.ThrowIfCancellationRequested();
// Pass token ke operasi async
var response = await _httpClient.GetAsync(url, cancellationToken);
return await response.Content.ReadFromJsonAsync<Data>(cancellationToken);
}
Menggunakan CancellationToken
// Di controller - ASP.NET Core auto-cancel saat request dibatalkan
[HttpGet]
public async Task<IActionResult> Get(CancellationToken cancellationToken)
{
var data = await _service.GetDataAsync(cancellationToken);
return Ok(data);
}
// Manual timeout
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
var result = await GetDataAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operasi di-cancel atau timeout");
}
Best Practices
1. Async All The Way
// β JANGAN - blocking async code
public string GetData()
{
return GetDataAsync().Result; // Deadlock risk!
}
// β
BENAR - async sampai atas
public async Task<string> GetDataAsync()
{
return await FetchDataAsync();
}
2. ConfigureAwait
// Di library code
public async Task<string> LibraryMethodAsync()
{
// ConfigureAwait(false) untuk library
var result = await SomeOperationAsync().ConfigureAwait(false);
return result;
}
// Di ASP.NET Core, biasanya tidak perlu ConfigureAwait
3. Hindari async void
// β JANGAN - exception tidak bisa di-catch
public async void BadMethod()
{
await SomethingAsync();
}
// β
BENAR - gunakan Task
public async Task GoodMethod()
{
await SomethingAsync();
}
// Exception: event handlers
button.Click += async (sender, e) =>
{
await HandleClickAsync();
};
4. ValueTask untuk Hot Paths
// Untuk operasi yang sering synchronous
public ValueTask<int> GetCachedValueAsync(string key)
{
if (_cache.TryGetValue(key, out int value))
{
return new ValueTask<int>(value); // No allocation
}
return new ValueTask<int>(FetchAndCacheAsync(key));
}
Common Mistakes
Deadlock
// β Deadlock di ASP.NET Framework (tidak di Core)
public ActionResult Index()
{
var data = GetDataAsync().Result; // DEADLOCK!
return View(data);
}
Fire and Forget
// β Exception hilang
public void StartBackgroundWork()
{
DoWorkAsync(); // Exception tidak akan terlihat!
}
// β
Lebih baik
public async Task StartBackgroundWork()
{
try
{
await DoWorkAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Background work failed");
}
}
Ringkasan
| Skenario | Gunakan |
|---|---|
| I/O bound (database, HTTP, file) | async/await |
| CPU bound | Task.Run() atau parallel |
| Sequential operations | await satu per satu |
| Independent operations | Task.WhenAll() |
| Race condition | Task.WhenAny() |
Async/await adalah salah satu fitur terpenting di C# modern. Kuasai konsep ini untuk membuat aplikasi yang responsif dan scalable!