Scheduling of async functions

# Paolo Amadini (7 years ago)

As I understand it, as currently specified, the first part of an async function, before the first await statement, is executed synchronously.

This is subject to subtle bugs when used with callbacks, which may be common with DOM event handlers. Take the following code example:

let someCondition = false;

async function onClick(event) {
   if (someCondition) {
     await Promise.resolve(); // Or any other async function.
   }
   event.stopPropagation();
}

let called = false;
onClick({ stopPropagation: () => called = true }).catch(console.error);

console.log("Called:", called);

The callback is called synchronously or not based on someCondition.

The behavior would be more consistent if the first part of the function was scheduled at the next microtask, in other words it would be like having an implicit await Promise.resolve(); at the beginning of every async function.

This would not allow async functions to invoke callbacks for DOM events at all, without wrapping them in a normal function. Using an async function as an event handler directly wouldn't be a good practice anyways, because if the async function throws an exception it would lead to an unhandled rejection, as if the .catch() in the example above was missing.

Is the current scheduling a deliberate choice?

I'm asking because async functions have just been implemented in the Mozilla JS engine, and it would be good to know that this is what we want before async functions get widespread use in our codebase.

# Jeremy Martin (7 years ago)

When mixing callback logic with async functions, it's up to the programmer to choose the callback semantics that they want.

Forcing execution of the async function into a future task prohibits logic that requires immediate invocation of a callback, whereas the existing semantics allow for both immediate and future invocation. At any rate, I assume this ship has sailed. This is a pretty foundational change to make in a Stage 4 proposal.

# Isiah Meadows (7 years ago)

You can already mitigate it with await undefined or similar.

On Wed, Nov 30, 2016, 12:16 Jeremy Martin <jmar777 at gmail.com> wrote:

When mixing callback logic with async functions, it's up to the programmer to choose the callback semantics that they want.

Forcing execution of the async function into a future task prohibits logic that requires immediate invocation of a callback, whereas the existing semantics allow for both immediate and future invocation. At any rate, I assume this ship has sailed. This is a pretty foundational change to make in a Stage 4 proposal.

I wouldn't quite argue that. Delaying the initial run by a tick won't be much of a change, IMHO. Most async functions don't require synchronous action because that's not what they're designed for. It's also generally bad practice to rely on promise scheduling order anyways.

I do agree with you that it is a bad idea, even irrespective of the feature's status, for the above reason of things like ev.preventDefault().