This post has been imported from my previous blog. I did my best to parse XML properly, but it might have some errors.
If you find one, send a Pull Request.
This post is a continuation of implementing a custom scheduler for your orchestrations. We saw that the Delay operation is either completed or results in a never ending task, that nobody ever completes. Could we make it easier and provide a better way for delaying operation?
The completed Delay operation was realized by
Task.CompletedTask
This is a static readonly instance of a task that is already completed. If you need to return a completed task, because the operation of your asynchronous method was done synchronously, this is the best way you can do it.
For cases where we don’t want continuations to be run, we used:
new TaskCompletionSource<object>().Task
which of course allocates both, the TaskCompletionSource instance and the underlying Task object. It’s not that much, but maybe, as there are only two states of the continuation: now or never, we could provide a smaller tool for this, that does not allocate.
You probably know, that you might create your custom awaitable objects, that you don’t need to await on Tasks only. Let’s take a look at the following class
public sealed class NowOrNever : ICriticalNotifyCompletion
{
public static readonly NowOrNever Never = new NowOrNever(false);
public static readonly NowOrNever Now = new NowOrNever(true);
NowOrNever(bool isCompleted)
{
IsCompleted = isCompleted;
}
public NowOrNever GetAwaiter()
{
return this;
}
public void GetResult() { }
public bool IsCompleted { get; }
public void OnCompleted(Action continuation) { }
public void UnsafeOnCompleted(Action continuation) { }
}
This class is awaitable, as it provides three elements:
Interested in async await, concurrency and you want to expand your knowledge?
Check out Async Expert! Together with Dotnetos I prepared something special for you!
Knowing what are these parts for, let’s take a look at different values provided by NowOrNever static fields
**NowOrNever** | **IsCompleted** | **OnCompleted/ UnsafeOnCompleted** |
Now | true | no action |
Never | false | no action |
As you can see, the completion is never called at all. For the Never case, that’s what we meant. What about Now? Just take a look above. Whenever IsCompleted is true, no continuations are attached, and the code is executed synchronously. We don’t need to preserve continuations as there are none.
Writing a custom awaitable class is not a day-to-day activity. Sometimes there are cases where this could have its limited benefit. In this NowOrNever case, this allowed to skip the allocation of a single task, although, yes, the created async state machine takes probably much more that a single task instance.