infinity
infinity

Reputation: 1920

Task.Delay(0) not asynchronous

I have the following code (yes, I could be simulating JavaScript setTimeout api)

    async void setTimeout(dynamic callback, int timeout)
    {
        await Task.Delay(timeout);
        callback();
    }

It looks like for timeout > 0, the setTimeout works asynchronously wherein the control is returned back to the callee on await and callback is invoked after the task runs asynchronously. BUT when timeout == 0, the function behaves in a synchronous manner (code always run past the await line in the same thread without a context switch). On further digging, it turns out that Task.Delay is implemented to behave this way (Task.Yield() versus Task.Delay(0))

Wondering if there is a way to make Task.Delay(0) asynchronous or an alternate solution to make my setTimeout function asynchronous when timeout is 0? (so that I could mimic JavaScript setTimeout functionality) I see discussions on using Task.FromResult(true) or Task.WhenAll, but they don't seem to work.

Indeed I can use Task.Delay(1) instead of Task.Delay(0) but it does not look organic.

Upvotes: 18

Views: 12176

Answers (1)

NeddySpaghetti
NeddySpaghetti

Reputation: 13495

The reason Task.Delay(0) does not run asynchronously is because the async-await state machine explicitly checks whether the task is completed and if it is, it runs synchronously.

You can try using Task.Yield(), which will force the method to return immediately and resume the rest of the method on the current SynchornizationContext. eg:

async void setTimeout(dynamic callback, int timeout)
{
    if(timeout > 0)
    {
        await Task.Delay(timeout);
    }
    else
    { 
        await Task.Yield();
    }

    callback();
}

Upvotes: 26

Related Questions