10 Await your own events
This chapter covers
- Creating Task objects that you control.
- TaskCompletionSource and TaskCompletionSource<T>.
- Completing a Task successfully, completing a Task with an error and cancelling a Task.
- Adapting old and non-standard asynchronous APIs to use tasks.
- Using TaskCompletionSource to implement asynchronous initialization.
- Using TaskCompletionSource to implement asynchronous data structures.
Until now we talked about using async-await to consume asynchronous APIs; in this chapter we’ll talk about writing your own. Common reasons for writing your own asynchronous APIS include adapting a non-Task based asynchronous API so it can be used with await, using await to asynchronously wait for events that happen in your application, or creating an async-await compatible thread-safe data structure, just to give a few examples (spoilers: we will write code for those examples later in this chapter).
Way back in chapter 3, in order to understand how the async and await keywords work, we took a method that used async-await and transformed it into an equivalent method that does exactly the same asynchronous operation but doesn’t use async and await. When we did this, we didn’t know how to create Task objects (yet), but we did know that await can be implemented by a callback (specifically, Task.ContinueWith). So instead of a Task we used callbacks to report the results of the operation. To make this change we changed the method signature from: