Before the creation of Task
in C#, Microsoft had implemented other means of creating asynchronous events. One was the Event-Asynchronous Pattern (EAP), where a callback method is fired when the asynchronous event finishes. Another is the Asynchronous Programming Model (APM) which uses IAsyncResult
and Begin/End style methods.
When the Task-based Asynchronous Pattern (TAP) was added, Microsoft made a concerted effort to add Task based versions of all of the async APIs in the BCL. However, they couldn't do everything, and on top of that, many third-party libraries were already out there using either EAP or APM. At the same time though, they realized the value of using the TAP and the value async
and await
bring. As a result, they created the TaskCompletionSource
. This serves as a wrapper to make non-TAP APIs work with TAP. Essentially, you create what many refer to as a puppet task. This is a task that really only exists to turn an EAP or APM (or other async pattern) method into a TAP method. For example, say you have a class, DownloadFile
. This class fires a DownloadComplete
event when the file is done. Normally you'd do something like:
DownloadFile myfile = new DownloadFile();
myfile.Complete += SomeMethodToHandleCompletion;
myfile.Download();
Then, at some point your method fires. That's great, but we want tasks. So what you could do is something like:
public Task<string> DownloadFileAsync(string url)
{
var tcs = new TaskCompletionSource<string>();
DownloadFile myfile = new DownloadFile(url);
myfile.Complete += (theData) =>
{
tcs.SetResult(theData);
};
return tcs.Task;
}
That's a simplistic example since I'm not handling exceptions or anything, but essentially, now I've turned an event handler (EAP) into something I can use with async/await by creating a "puppet task" that simply does the bidding of the event. When the event finishes, we simply say "hey the Task is done too". As a result, the following works:
string downloadedFile = await DownloadFileAsync("http://www.some.url.example");