Menu

TypeScript Async Await: Tipler, Hatalar ve Paralel Çağrılar

async ve await TypeScript'te nasıl tiplenir: bir async fonksiyon Promise<T> döndürür, await onu açar, hatalar try/catch ile yakalanır, top-level await bir ES modülü gerektirir; ayrıca tek tek beklemek, paralel beklemek ve forEach tuzağı arasındaki fark.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

TypeScript'te bir async fonksiyon her zaman bir promise döndürür: gövdesi bir string döndürüyorsa tipi Promise<string> olur. Fonksiyonun içinde await, bir Promise<T>'yi açıp T yapar.

Çalışma zamanı davranışı (await'in neyi duraklattığı, event loop) JavaScript'e aittir ve JavaScript async/await sayfasında anlatılır. TypeScript'in payı giren ve çıkan tiplerdir.

Async Fonksiyonların Dönüş Tipleri

Bir async fonksiyonun belirtilen dönüş tipi, gövde düz değeri döndürse bile Promise<...> olmalıdır. Yazmazsanız TypeScript onu çıkarır.

async function count(): number yazmak TS1064 hatasıdır (The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'?). Awaited<T> utility type'ı açılmış tipi verir: Awaited<ReturnType<typeof count>> sonucu number olur.

await'i Unutmak

Eksik bir await, elinizde bir T yerine bir Promise<T> bırakır. TypeScript bunların çoğunu yakalar, çünkü tipler artık uymaz:

if (ok) hatası gerçektir: bir promise nesnesi her zaman truthy'dir, bu yüzden erişim izni verilmiştir. TypeScript bunu TS2801 olarak bildirir. Promise'i bir boolean değişkene atamak ya da promise'te olmayan bir özelliği okumak da derlenmez. Sonucunu yok saydığınız bir çağrı (save(user);) ise yakalanmaz; bunu typescript-eslint'in no-floating-promises kuralı kapsar.

try/catch ile Hata Yönetimi

Reject olan bir promise, await'in hata fırlatmasına neden olur, bu yüzden sıradan try/catch çalışır. strict altında catch içindeki değişken unknown'dır ve .message okumadan önce onu daraltırsınız:

Bir async fonksiyonun içindeki throw, çağrıldığı yerde hata fırlatmak yerine fonksiyonun promise'ini reject eder. Özel hata class'ları ve hata fırlatmak yerine sonuç döndürmek gibi başka kalıplar hata yönetimi sayfasındadır.

Top-Level await

Herhangi bir fonksiyonun dışındaki await yalnızca bir ES modülünde çalışır. CommonJS olarak derlenen bir dosya (bu örneklerde ve "type": "module" olmayan Node projelerinde durum budur) onu reddeder:

index.ts(2,14): error TS1309: The current file is a CommonJS module and cannot use 'await' at the top level.

Bu sayfadaki her örnekte olduğu gibi kodu async bir main fonksiyonuna sarın ve onu çağırın. Bir ES modülü projesinde (package.json içinde "type": "module" ile module değeri node16 ya da nodenext, veya bir bundler için module: "esnext") top-level await kullanılabilir.

Sıralı ve Paralel

Her await, sonraki satır başlamadan önce kendi promise'ini bekler. Birbirinden bağımsız çağrılar için önce hepsini başlatın, sonra Promise.all ile birlikte bekleyin:

Sıralı bölümde b, a'dan önce bitemez çünkü henüz başlamamıştır. Paralel bölümde önce d biter ve toplam süre çağrıların toplamı değil, yaklaşık olarak en uzun çağrının süresidir. Promise.all yine de sonuçları girdi sırasıyla, bir tuple olarak tiplenmiş şekilde döndürür.

forEach Tuzağı

forEach, async bir callback'in döndürdüğü promise'i yok sayar, bu yüzden işi hiçbir şey beklemez:

await ile for...of öğeleri tek tek işler; map ile Promise.all onları paralel çalıştırır ve hepsini bekler. forEach ikisini de yapmaz ve TypeScript uyarmaz, çünkü void döndürecek şekilde tiplenmiş bir callback, promise döndüren bir callback'i kabul eder.

for await ile Async İterasyon

for await...of, async generator gibi bir async iterable üzerinde döner ve her değeri bekler:

async function* pages(total: number): AsyncGenerator<string[]> {
    for (let page = 1; page <= total; page++) {
        await new Promise((r) => setTimeout(r, 10));
        yield [`item ${page}a`, `item ${page}b`];
    }
}

async function main() {
    for await (const batch of pages(3)) {
        console.log(batch.join(", ")); // batch: string[]
    }
}
main();

Eleman tipi, generator'ın AsyncGenerator<T> işaretlemesinden ya da işaretlemeyi yazmadığınızda tip çıkarımından gelir.

Sıkça Sorulan Sorular

TypeScript'te bir async fonksiyonun dönüş tipi nedir?

Her zaman bir promise. number döndüren bir async fonksiyonun dönüş tipi Promise<number>, hiçbir şey döndürmeyeninki Promise<void> olur. async function f(): number yazmak TS1064 hatasıdır ve derleyici Promise<number> önerir.

TypeScript'te top-level await nasıl kullanılır?

Top-level await yalnızca bir ES modülünde çalışır: module değeri es2022, esnext, system, preserve ya da Node'un ESM olarak gördüğü bir dosyada node16/node18/node20/nodenext olmalı ve target en az es2017 olmalıdır. Bir CommonJS dosyasında bu TS1309 hatasıdır. Her yerde çalışan çözüm bir async main fonksiyonudur: async function main() { ... } main();.

TypeScript'te async/await ile hataları nasıl yönetirim?

await ifadesini try/catch ile sarın. strict altında yakalanan değer unknown olarak tiplenir, bu yüzden önce daraltın: if (e instanceof Error) console.log(e.message). Hiç await edilmeyen ya da yakalanmayan bir reject, ele alınmamış bir reddedilmeye dönüşür.

TypeScript'te async çağrıları paralel nasıl çalıştırırım?

Önce bütün promise'leri başlatın, sonra hepsini birlikte bekleyin: const [a, b] = await Promise.all([loadA(), loadB()]). await loadA(); await loadB(); yazmak onları art arda çalıştırır. Promise.all, elde edilen tuple'da her sonucun tipini korur.

async neden forEach içinde çalışmıyor?

forEach callback'i çağırır ve döndürdüğü şeyi yok sayar; bu yüzden async bir callback'in promise'leri hiç beklenmez: döngü hemen biter ve ondan sonraki kod, iş bitmeden çalışır. Tek tek iş için await ile for...of, paralel iş için await Promise.all(items.map(async (x) => ...)) kullanın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA