Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 227
infinity Avatar asked Oct 29 '15 06:10

infinity


People also ask

Is Task delay async?

Note that since the task that calls the Delay method executes asynchronously, the parent task must wait for it to complete by using the await keyword.

What happens when you don't await a task?

If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.

Does task delay use a thread?

Task. Delay() is asynchronous. It doesn't block the current thread.

Does await task delay block thread?

await Task. Delay(1000) doesn't block the thread, unlike Task. Delay(1000).


1 Answers

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();
}
like image 93
NeddySpaghetti Avatar answered Oct 18 '22 22:10

NeddySpaghetti