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.
Sometimes you need to write your own async-await compatible asynchronous operations. You need this if you want to adapt a non-Task based asynchronous API so it can be used with await, use await to asynchronously wait for events that happen in your application or create asynchronous versions of data structures, 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 the async and await keywords. 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:
Task<int> GetBitmapWidth(string path)
To: