In my experiences, TaskCompletionSource
is great for wrapping old asynchronous patterns to the modern async/await
pattern.
The most beneficial example I can think of is when working with Socket
. It has the old APM and EAP patterns, but not the awaitable Task
methods that TcpListener
and TcpClient
have.
I personally have several issues with the NetworkStream
class and prefer the raw Socket
. Being that I also love the async/await
pattern, I made an extension class SocketExtender
which creates several extension methods for Socket
.
All of these methods make use of TaskCompletionSource<T>
to wrap the asynchronous calls like so:
public static Task<Socket> AcceptAsync(this Socket socket)
{
if (socket == null)
throw new ArgumentNullException("socket");
var tcs = new TaskCompletionSource<Socket>();
socket.BeginAccept(asyncResult =>
{
try
{
var s = asyncResult.AsyncState as Socket;
var client = s.EndAccept(asyncResult);
tcs.SetResult(client);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, socket);
return tcs.Task;
}
I pass the socket
into the BeginAccept
methods so that I get a slight performance boost out of the compiler not having to hoist the local parameter.
Then the beauty of it all:
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
listener.Listen(10);
var client = await listener.AcceptAsync();