typescript - Bir Javascript async fonksiyonunda Promise nasıl döndürülür? Async metod neden döndürülen Promise'i sarmıyor?


Aşağıdaki kodum var. Amacım bir görev başlatmak (gerçekten başlayabilmesi için birkaç await çağrısı içeren). Görev başladığında, kullanıcıya görevin başladığını ve sonuç için beklediğini belirtmek için UI'yi güncellemem gerekiyor. Sonuç daha sonra gelecek, bu yüzden uygulamanın dönen sonuçla UI'yi ne zaman güncelleyeceğini bilmesi için başka bir Promise döndürmek istiyorum:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<number>(r => {
        globalThis.setTimeout(() => r(100), 3000);
    });
    return finishedPromise;
}

(async() => {
    // resultPromise is number instead of Promise<number>
    const resultPromise = await startSomethingAsync();

    // My intention: update UI after starting the task
    console.log("Task started");

    // Then when the result is there, do something else
    // Currently this is not compiled because resultPromise is not a Promise
    resultPromise.then(r => console.log("Result arrived: " + r));
})();

Ancak yukarıdaki kod çalışmıyor. Bir şekilde resultPromise Promise<number> yerine number (çözüldükten sonra 100) oluyor. TypeScript ayrıca startSomethingAsync'in Promise<number> döndürdüğünü, Promise<Promise<number>> döndürdüğünü değil, tanıyor.

Bu neden oluyor? async yöntemi, döndürülen Promise dışında başka bir Promise ile sarmalamamalı mı? Yapmaya çalıştığım şeyi nasıl başarabilirim ve bu "iyi" bir yöntem mi?


Bunu kendim sarmayı bile denedim:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<Promise<number>>(r1 => r1(new Promise<number>(r2 => {
        globalThis.setTimeout(() => r2(100), 3000);
    })));
    return finishedPromise;
}

Fonksiyon hâlâ Promise<number> döndürüyor, Promise<Promise<number>> yerine.

Cevap #1

Yorumlarda tartıştığımız gibi - ortaya çıkan promise'i bir fonksiyon veya obje içinde sarman gerekiyor.

async function startSomethingAsync() {
  await new Promise(r => setTimeout(r, 1000));

  const finishedPromise = new Promise(r => {
    setTimeout(() => r(100), 3000);
  });
  return () => finishedPromise;
}

(async () => {
  const resultPromise = await startSomethingAsync();
  resultPromise().then((r) => console.log("Result arrived: " + r));
})();

Cevap #2

Bu neden oluyor? Asenkron yöntem, döndürülen Promise'in dışında başka bir Promise sarmalamamalı mı?

Hayır. Eğer bir async fonksiyondan bir promise döndürürseniz, async fonksiyonu çağırarak oluşturulan promise yalnızca o promise ile çözümlenir (başka bir promise'in sonuçlanmasını bekler ve kendi yerine onun tamamlanmasını ya da reddedilmesini alır; promise terminolojisi hakkında daha fazla bilgiyi blogumda bulabilirsiniz). Promise, bir promise ile yerine getirilmek üzere ayarlanmaz. Bir promise asla bir promise ile yerine getirilmez (hangi async fonksiyonları kullandığınızdan bağımsız olarak).

Yapmaya çalıştığım şeyi nasıl başarabilirim ve bu "iyi" bir desen mi?

Eğer startSomethingAsync içindeki promise’i kullanmak istiyorsanız, basitçe onu await etmeyin:

(async() => {
    const resultPromise = startSomethingAsync();
    //                    ^−−− no `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

Şunu unutmayın ki, eğer startSomethingAsync işe başlamadan önce asenkron bir gecikmeye sahipse (bu oldukça olağandışı olur), "Görev başladı" kaydı bu asenkron gecikmeden önce gerçekleşir. Bunu istemiyorsanız, bu kısmı fonksiyondan ayırın, böylece "Görev başladı" kaydınızı ortada alabilirsiniz. Örneğin, başlangıç kısmını yapan ayrı bir fonksiyonunuz olabilir ve ardından bundan herhangi bir şeyi startSomethingAsync fonksiyonuna aktarabilirsiniz:

(async() => {
    const resultPromise = startSomethingAsync(await theStuffBefore());
    //                                        ^^^^^

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

Alternatif olarak, eğer süreciniz çok adımlıysa, her adım bir sonraki adım için bir fonksiyon (veya yöntemleri olan bir nesne) döndürebilir:

(async() => {
    const nextStep = await startSomethingAsync();
    const resultPromise = nextStep(); // No `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    console.log("Result arrived: " + r);
})();

(Aslında, bu bir async generator kullanmaya çok benziyor.)

Bunların hepsi bir tema üzerinde çeşitli varyasyonlardır, ama çıkarılması gereken mesaj şudur ki bir söz, başka bir sözle asla yerine getirilemez. new Promise ile elde ettiğiniz fonksiyonların standart adlarının resolve ve reject olduğunu, fulfill ve reject olmadığını fark edeceksiniz. Bunun nedeni, bir sözü resolve'a geçirmeniz (veya bir async fonksiyondan birini döndürmeniz, bu aslında aynı şeydir) durumunda, sözün yerine başka bir sözle yerine getirmek yerine, o sözü diğer söze çözümler.

İlgili makaleler