Search code examples
javascripttypescriptpromisemethod-chaining

How to return a Promise in a Javascript async function? Why does Async method not wrap the returned Promise?


I have the following code. My intention is to start a task (which involves a few await calls before it can actually start). When it finishes starting it, I need to update the UI to tell user that the task has started and is waiting for result. Result will come in later so I want to return another Promise so the app knows when to update the UI with returned result:

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));
})();

However the above code does not work. Somehow resultPromise is number (100 after resolved) instead of Promise<number>. TypeScript also recognize startSomethingAsync returning Promise<number> instead of Promise<Promise<number>>.

Why is this happening? Shouldn't the async method wrap another Promise outside of the returned Promise? How do I achieve what I am trying to do and is it a "good" pattern?


I even tried wrapping it by myself:

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;
}

The function still returns Promise<number> instead of Promise<Promise<number>>.


Solution

  • As we discussed in the comments - you have to wrap the resulting promise in a function or an object.

    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));
    })();