await setTimeout in async functions

# Jérémy Judéaux (7 years ago)

Let's take a simple asynchronous function

const asyncFunc = (...args) => new Promise((resolve) => {
  setTimeout(() => resolve(computeResult(...args)), 1000);
});

One problem in this example is that any error thrown by computeResult will not be correctly handled. This can be solved with an async function:

const delay = (duration) =>
  new Promise(resolve => setTimeout(resolve, duration));

const asyncFunc = async (...args) => {
  await delay(1000);
  return computeResult(...args);
};

It's always better to handle errors. So I would not be surprised if a similar pattern become favoured over the use of setTimeout, when writing and learning asynchronous functions.

Writing a delay function is easy. But a standardised statement (await*delay, await.nextTick or whatever name or syntax) would highlight these patterns. It would also be an opportunity to standardise setTimeout, setImmediate and such, without risking incompatibilities (I've seen that point reported in a recent email). Cancellation would probably be a sensible point, but it may also be left to a more global solution about cancelling async functions.

I’ve found some old discussions about a Promise-returning delay function, showing that the (lack of?) concept of event-loop in ES would be a problem.

Is it worth discussing again, in the context of async functions?

.

# Andrea Giammarchi (7 years ago)

Why not using fill setTimeout API, also granting you args are those passed at the invocation time and no possible mutation capable of affecting computeResult could happen later on?


const asyncFunc = (...args) => new Promise((resolve) => {

  setTimeout(resolve, 1000, computeResult(...args));

});

Otherwise you can always do the following


const asyncFunc = (...args) =>

  new Promise(r => setTimeout(r, 1000))

  .then(() => computeResult(...args));

isn't it?

# T.J. Crowder (7 years ago)

On Tue, Feb 28, 2017 at 7:47 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

Why not using fill setTimeout API, also granting you args are those passed at the invocation time and no possible mutation capable of affecting computeResult could happen later on?


const asyncFunc = (...args) => new Promise((resolve) => {

  setTimeout(resolve, 1000, computeResult(...args));

});

That would call computeReult before the timeout fired, whereas apparently it should be after the delay.

Your second one, though, is nice and simple. Using Jérémy's delay, it looks like this:

const asyncFunc = (...args) =>
  delay(1000).then(() => computeResult(...args));

-- T.J. Crowder

# Andrea Giammarchi (7 years ago)

In the first example, I haven't written this by accident:

also granting you args are those passed at the invocation time and no

possible mutation capable of affecting computeResult could happen later on?

which is why I've shown both examples, the before setTimeout(resolve, 1000, computeResult(...args)); and the after ;-)

# T.J. Crowder (7 years ago)

On Tue, Feb 28, 2017 at 8:14 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

In the first example, I haven't written this by accident:

also granting you args are those passed at the invocation time and no possible mutation capable of affecting computeResult could happen later on?

Okay. With respect, that sentence is really unclear, esp. given the context that it was obvious from the example that it's important that computeResult isn't called until after the delay. But we're all on the same page now.

-- T.J. Crowder

# Andrea Giammarchi (7 years ago)

it was obvious from the example that it's important that computeResult

isn't called until after the delay

I was just underlying possible side effects. TBH, I don't even know why forcing a delay to an async function would be needed but yeah, definitively on the same page.

I am also a bit against underpowered patterns, like a delay(1000) over setTimeout since the latter one can be canceled, a delay(1000) without cancelable Promises is a curse, IMO

¯_(ツ)_/¯

# Jérémy Judéaux (7 years ago)

I don't even know why forcing a delay to an async function would be needed

In my opinion, for the same reasons calling setTimeout would have been needed: batching several calls together for a request, simulating i/o latency, delaying the rest of an expensive computation later in the event loop, racing with another operation for a timeout, …

I tend to see setTimeout as an old function, not meant to work well with recent ES features (Promises and async functions), having disadvantages (for example, error handling), and AFAIK not standard.

On the other hand, it’s easy to write or find a Promise-returning delay. And there is the cancellation issue (but I have the intuition that a “delay function” now will be compatible with any “cancellation solution” later).

I believe that, at some point, the “delaying” feature should be reviewed. Maybe not now.

# Andrea Giammarchi (7 years ago)

AFAIK not standard.

FYI it's standard for the DOM world.

www.w3.org/TR/2011/WD-html5-20110525/timers.html#timers

About cancelability, if the purpose is to delay stressful operations, I would definitively like to cancel that whenever is needed ;-)

A generic Promise based delay(ms) seems to be trivial enough I don't think specs should be bothered.

I mean, this is all it takes, right?

const delay = ms => new Promise(r => setTimeout(r, ms));