Home > database >  Can I run an async function synchoronously without await assuming it is the last action?
Can I run an async function synchoronously without await assuming it is the last action?

Time:10-18

Let's say I have two async functions:

    async f(): Promise<void> {
        flag = true;
        await computation();
    }

    async g(): Promise<void> {
        doPreparation();  // synchronous
        f();
    }

As far as I understand, if I prepend f() call with await, then at this point the runtime is free to schedule the execution of f at a later time and some other code may be chosen to be run meanwhile.

What I want is to make g pass execution to f synchronously, i.e. flag = true should run strictly after doPreparation() without yields (I don't expect computation() to be run synchronously too). This seems like a fair request since f is the last action in g.

Is there a guaranteed way to run an async f this way?

CodePudding user response:

Either way, with await f() or just f(), then f() will be called immediately when the interpreter hits that line of code.

The difference for await is when the NEXT line of code is executed.

For the last line of code in your async function, you can just do:

return f();

There is no need to use await on the last line of the async function unless you also have it surrounded by a try/catch and are trying to catch the rejection locally within the function.

So, these two are identical:

async g(): Promise<void> {
    doPreparation();   // synchronous
    return f();        // asynchronous, f() returns a promise
}

and

async g(): Promise<void> {
    doPreparation();   // synchronous
    let x = await f(); // asynchronous, f() returns a promise
    return x;
}

Or, if the promise that f() returns has an undefined resolved value, then this is also the same:

async g(): Promise<void> {
    doPreparation();   // synchronous
    await f();         // asynchronous, f() returns a promise
}

This, would NOT be the same:

async g(): Promise<void> {
    doPreparation();  // synchronous
    f();              // asynchronous, f() returns a promise
}

because in this case, the promise that g() returns would resolve BEFORE the promise that f() returns was resolved. In fact, the promise that g() returns would be entirely disconnected from the f() promise. In this case, f() is fire and forget with no way for anyone to know when it's done or any way for anyone to catch/handle its error. If f() rejects in this last scenario, then your app will get an unhandled rejection which is never a good thing.

CodePudding user response:

Code like that is possible to use, but it has downsides:

  • If f rejects, it won't be possible for it to be caught by the caller of g. It'd usually make more sense to return the Promise so that the caller can do something with it if desired. If you don't want to return it, then to avoid an unhandled rejection, add a .catch onto the call of f. Because you aren't awaiting or returning anything, g may as well be a plain function/method.

    g() {
      doPreparation();
      f()
          .catch(handleErrors);
    }
    
  • f will still run immediately. Just like the Promise constructor, there won't be any delay at all between when the function is called and when the function runs - at least up until an await is encountered. The code in the question will yield control back to the caller of g only after computation has finished computing; if computation is expensive, that could be a problem. Better to use either a web worker to put the task onto a separate thread, or to at least use setTimeout(computation) plus the required error handling.

  • Related