C# async void ile async Task arasındaki temel fark, çağıran kodun işlemi izleyip izleyememesidir. async Task, işlemin tamamlanmasını await ile beklemeye ve oluşan istisnaları çağrı zincirinde yönetmeye imkân verir. async void ise çağıran koda tamamlanmayı temsil eden bir değer döndürmez.
Bu nedenle bir asenkron metodu değerlendirirken yalnızca async anahtar sözcüğüne bakmak yeterli değildir. Metodun imzasını, nasıl çağrıldığını, beklenip beklenemediğini, istisnaların nereye aktığını ve nasıl test edileceğini birlikte incelemek gerekir.
async void ve async Task arasındaki temel fark nedir?
async Task biçimindeki bir metot, devam eden işlemi temsil eden bir Task nesnesi döndürür. Çağıran kod bu nesneyi await ederek işlemin bitmesini bekleyebilir:
async Task VerileriKaydetAsync()
{
await DosyayaYazAsync();
}
await VerileriKaydetAsync();
Console.WriteLine("Kayıt tamamlandı.");
Son satırdaki mesaj, asenkron işlem tamamlandıktan sonra çalışır. DosyayaYazAsync sırasında bir istisna oluşursa bu hata döndürülen Task üzerinden izlenebilir ve çağıran tarafta uygun bir try/catch bloğuyla yönetilebilir.
async void ise işlemi temsil eden bir nesne döndürmez:
async void VerileriKaydet()
{
await DosyayaYazAsync();
}
VerileriKaydet();
Console.WriteLine("Metot çağrıldı.");
Burada mesajın yazılması, kayıt işleminin tamamlandığını göstermez. Çağıran kodun elinde bekleyebileceği bir Task bulunmadığı için tamamlanma anını doğrudan takip etmek, sonucu çağrı zincirine katmak ve hatayı aynı akışta yönetmek zorlaşır.
async ve await kullanmak, kodun otomatik olarak paralel çalışacağı veya belirli bir iş parçacığında yürütüleceği anlamına gelmez. Bu sözdizimi öncelikle beklemeler içeren işlemlerin akışını düzenler. Doğru zihinsel model şu beş soruya dayanır: Metodun imzası nedir, nasıl çağrılır, çağıran kod onu bekleyebilir mi, istisna nasıl taşınır ve davranış nasıl test edilir?
Hangi async metotlar await edilebilir?

Bir metodun async olması, o metoda yapılan çağrının mutlaka await edilebileceği anlamına gelmez. Çağıran kod açısından belirleyici olan dönüş tipidir. Yaygın üç imza şöyle karşılaştırılabilir:
| Dönüş tipi | Çağıran kodun bekleyebilmesi | İstisna akışının yönetimi | Tipik kullanım alanı |
|---|---|---|---|
Task |
await ile beklenebilir |
Döndürülen görev üzerinden çağıran koda taşınabilir | Sonuç üretmeyen genel asenkron işlemler |
Task<T> |
await ile beklenebilir |
Döndürülen görev üzerinden çağıran koda taşınabilir | Bir sonuç üreten asenkron işlemler |
void |
Metot çağrısı beklenemez | Çağıran kod tarafından bir görev üzerinden izlenemez | Temel olarak uygun imzayı gerektiren event handler’lar |
Task<T>, tamamlanma bilgisinin yanında bir sonuç da taşır:
async Task<int> SayiGetirAsync()
{
await Task.Delay(100);
return 42;
}
int sonuc = await SayiGetirAsync();
Buna karşılık aşağıdaki kullanım geçerli değildir; çünkü BildirimGoster çağrısı beklenebilir bir değer üretmez:
async void BildirimGoster()
{
await BildirimHazirlaAsync();
}
// await BildirimGoster(); // Beklenebilir değer yoktur.
Genel uygulama mantığında async Task ve sonuç gerekiyorsa async Task<T> tercih edilmelidir. async void bütünüyle hatalı değildir; dönüş tipinin void olmasını gerektiren event handler imzaları özel ve meşru bir kullanım alanı oluşturur.
try/catch neden async void çağrısındaki hatayı yakalayamaz?
async Task metotlarında istisna, dönen Task nesnesi üzerinde tutulur ve çağıran kod await kullandığında yeniden oluşturulur. Bu nedenle await ifadesi bir try bloğunun içindeyse hata, çağıran katmanda yakalanabilir. async void metodunda ise çağırana aktarılabilecek bir Task bulunmaz; metot beklenemez ve istisna doğal biçimde dışarıdaki catch bloğuna taşınamaz.
Çağıran metot
└── await edilen Task
└── hata Task üzerinde tutulur
└── await noktasındaki try/catch gözlemler
Çağıran metot
└── async void çağrısı
└── beklenebilir Task yok
└── dış try/catch akışın tamamını kapsayamaz
Bu yüzden aşağıdaki iki çağrı görünüşte benzer olsa da hata akışları farklıdır:
await IslemAsync(): Çağıran kod tamamlanmayı ve istisnayı takip eder.IslemVoidAsync(): Çağıran kod yalnızca metodu başlatır; tamamlanma, hata ve uygulamanın kapanma zamanı arasında doğrudan bir takip ilişkisi kurulmaz.
Aynı durum async Task metodu çağrılıp await edilmediğinde de kısmen ortaya çıkar. Bu kez çağıran kodun elinde bir Task vardır; ancak onu beklemediği için hata o anda dışarıdaki try/catch bloğuna ulaşmaz. Teşhis için sorulacak ilk soru şudur: Çağıran kod gerçekten bekliyor mu? Beklemiyorsa hata yönetimi ve işlem ömrü çağıran katmanın kontrolünden çıkmış olabilir.
İki kısa C# örneğiyle bekleme ve hata akışını görün

1. async Task ile hata çağırana taşınır
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
await GecikmeliIslemAsync();
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Hata yakalandı: {ex.Message}");
}
}
static async Task GecikmeliIslemAsync()
{
await Task.Delay(100);
throw new InvalidOperationException("İşlem tamamlanamadı.");
}
}
Beklenen çıktı:
Hata yakalandı: İşlem tamamlanamadı.
Burada await, GecikmeliIslemAsync metodunun tamamlanmasını bekler. İstisna, await noktasında yeniden oluşturulduğu için dışarıdaki catch bloğu çalışır.
2. async void ile hata yönetimi metodun içine taşınır
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
try
{
BaslatVeHataYakala();
Console.WriteLine("Çağıran metot devam etti.");
}
catch (Exception)
{
Console.WriteLine("Dış catch çalıştı.");
}
await Task.Delay(300);
}
static async void BaslatVeHataYakala()
{
try
{
await Task.Delay(100);
throw new InvalidOperationException("async void içinde hata.");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"İç catch çalıştı: {ex.Message}");
}
}
}
Beklenen çıktı:
Çağıran metot devam etti.
İç catch çalıştı: async void içinde hata.
Dış try/catch burada hatayı yakalamaz; çünkü çağıran kodun bekleyebileceği bir Task yoktur. Son Task.Delay yalnızca konsol uygulamasının, fire-and-forget işlemi tamamlanmadan kapanmasını önlemek içindir. Gerçek bir uygulamada bu kontrol kopukluğu; eksik log, yarım kalan işlem veya beklenmedik yaşam döngüsü sorunlarına yol açabilir. Bu nedenle async void kullanılıyorsa hata yönetimi metodun içinde açıkça yapılmalıdır.
async void ne zaman meşru bir tercihtir?
async void, esas olarak çağrı sözleşmesinin void dönüşünü zorunlu kıldığı asenkron event handler senaryolarında meşru bir tercihtir. Kullanıcı bir düğmeye bastığında, pencere yüklendiğinde veya bir arayüz olayı tetiklendiğinde framework, handler metodunu belirli bir imzayla çağırabilir. Bu imza Task döndürmeye izin vermiyorsa handler içinde await kullanılabilen seçenek async void olabilir.
Bu akışta olay başlatılır, handler çalışır ve handler içindeki asenkron işlem await noktasında beklemeye geçebilir. Ancak olayı başlatan kod çoğu zaman handler’ın tamamlanmasını bir Task üzerinden bekleyemez. Bu nedenle hata yönetimi de normal bir async Task çağrısından farklı düşünülmelidir. Handler’ın kendi sınırları içinde uygun bir try/catch kullanmak, hatayı kullanıcıya göstermek, kayıt altına almak veya uygulamanın ilgili hata mekanizmasına aktarmak gerekebilir.
Bunun dışında uygulama servislerinde, yardımcı metotlarda, iş kurallarında ve test edilecek kodlarda genellikle async Task daha izlenebilir bir çağrı zinciri sağlar. Çağıran kod işlemi await edebilir, tamamlanmayı bekleyebilir ve oluşan istisnayı kendi hata yönetimi içinde ele alabilir. Bu yaklaşım, asenkron akışın hangi noktada devam ettiğini ve işlemin ne zaman bittiğini anlamayı kolaylaştırır.
Seçim kontrol listesi
- Bu metot gerçekten bir event handler mı? Framework tarafından zorunlu tutulan bir imza yoksa
async Taskseçeneğini değerlendir. - Çağıran kod tamamlanmayı beklemeli mi? Beklemeliyse metot, beklenebilir bir
Taskdöndürmelidir. - Hata üst katmana taşınmalı mı? Taşınacaksa
async Taskçağrı zincirini korur;async voidbu zinciri kesebilir.
Birim testlerde async Task seçimi ve teşhis şeması
Birim testlerde async Task yaklaşımı tercih edilir; çünkü test metodu asenkron işlemi await edebilir, işlemin tamamlanmasını bekleyebilir ve başarısızlıkları test akışına taşıyabilir. Böylece test, işlem henüz bitmeden tamamlanmış sayılmaz. Ayrıca hata oluştuğunda istisnanın hangi çağrıdan geldiğini izlemek daha kolay olur.
Test edilemeyen veya beklenmedik biçimde başarılı görünen bir asenkron akışta şu sırayla ilerle:
- İmza: Test edilen metot
async voidmı,async Taskmı? İlk kontrol dönüş tipidir. - Çağrı: Çağıran kod gerçekten bir
Taskalıyor mu? Alınmıyorsa tamamlanma ve hata bilgisi kaybolabilir. - Bekleme: Elde edilen
Taskawaitediliyor mu? Edilmeyen çağrı, testin asenkron işlemi beklemeden bitmesine yol açabilir. - İstisna:
try/catchbloğu doğru çağrı zincirini kapsıyor mu?awaitedilmeyen veyaasync voidbir akışta dışarıdaki blok beklenen hatayı yakalayamayabilir. - Test tamamlanması: Test altyapısı, metodun döndürdüğü
Tasktamamlanana kadar gerçekten bekliyor mu?
Bu süreci İmza, çağrı, bekleme, istisna, test teşhis şeması olarak düşünebilirsin: önce metodun dönüş tipini, sonra çağrının Task üretip üretmediğini, ardından await kullanımını, hata kapsamını ve son olarak testin tamamlanma koşulunu kontrol et.
Düzenli pratik yaparken hangi C# konularını ne ölçüde bildiğini görmek için C# bilgi testlerinden yararlanabilirsin. Bu testler bir eğitim programı değil, mevcut seviyeni kontrol etmeye ve çalışma konularını belirlemeye yardımcı olan bir araçtır.
Kısa seçim kontrol listesi
- Event handler imzası zorunluysa
async voiddüşünülebilir. - İş mantığı ve yardımcı metotlarda
async Taskkullan. - Test edilecek metotların tamamlanabilir ve beklenebilir olmasına dikkat et.
- Hatanın hangi katmanda ele alınacağını önceden belirle.
Sık Sorulan Sorular
async void metot await edilebilir mi?
Hayır. async void bir Task döndürmediği için çağıran kod tarafından doğrudan await edilemez. Bu nedenle tamamlanma ve hata takibi sınırlıdır.
async Task çağrısını await etmezsem ne olur?
Asenkron işlem arka planda devam edebilir; ancak çağıran kod tamamlanmasını beklemez. Test veya üst katman erken bitebilir ve oluşan istisna beklenen hata akışına taşınmayabilir.
async void içindeki istisna neden dışarıdaki try/catch bloğuna ulaşmayabilir?
Çağıran kod async void metodunu bekleyemediği için istisna, normal Task çağrı zinciri üzerinden dışarı taşınmaz. Hatanın handler içinde veya ilgili özel mekanizmada ele alınması gerekir.
Event handler dışında async void kullanmak ne zaman sorun oluşturur?
Çağıran kodun tamamlanmayı beklemesi, sonucu izlemesi veya hatayı üst katmana aktarması gerektiğinde sorun oluşturur. Bu durumlarda async Task daha güvenli ve izlenebilir bir seçimdir.
Asenkron bir metot birim testte neden async Task döndürmelidir?
Test metodu böylece işlemi await edebilir, tamamlanmasını bekleyebilir ve oluşan istisnayı test sonucuna yansıtabilir. Bu, testin işlem bitmeden başarılı görünmesini önlemeye yardımcı olur.
Özetle, async void event handler gibi zorunlu imza gerektiren dar bir bağlamda kullanılabilir; uygulamanın geri kalanında ise async Task tercih etmek bekleme, hata yakalama ve test edilebilirlik açısından daha sağlıklı bir akış sağlar.