8

I want to use TPL with a existing API, RestSharp to be specific, so I can use continuations.

But this means I have to wrap a API that doesn't take the classical .NET approach to async, but instead implements callbacks. Take some code like this:

var client = new RestClient("service-url");
var request = new RestRequest();

client.ExecuteAsync<List<LiveTileWeatherResponse>>(request, 
    (response) =>
    {
        ...
    });

So here I want to wrap the ExecuteAsync in TPL, if it's possible. But I can't for the life of me, figure out how to do it.

Any ideas?

Claus Jørgensen
  • 25,882
  • 9
  • 87
  • 150

2 Answers2

12

TPL provides TaskCompletionSource class which allows you to expose pretty much anything as a Task. By calling SetResult or SetException, you can cause the task to succeed or fail. In your example, you could probably do something like:

static Task<T> ExecuteTask<T>(this RestClient client, RestRequest request)
{
    var tcs = new TaskCompletionSource<T>();
    client.ExecuteAsync<T>(request, response => tcs.SetResult(response));
    return tcs.Task;
}

You can then use it:

var task = client.ExecuteTask<List<LiveTileWeatherResponse>>(request);
foreach (var tile in task.Result)
{}

Or, if you want to chain tasks:

var task = client.ExecuteTask<List<LiveTileWeatherResponse>>(request);
task.ContinueWith(
    t => 
    {
        foreach (var tile in t.Result)
        {}
    }
);

You can read more about TaskCompletionSource at http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx

Bojan Resnik
  • 7,320
  • 28
  • 29
  • It's definitively the right approach. But sadly, the ContinueWith is called to early, and the `t.IsCompleted` is set `false` :( – Claus Jørgensen Aug 09 '11 at 02:29
  • That does not matter - the delegate specified in ContinueWith will be scheduled and executed after the task completes, whenever that may be. t.IsCompleted should always be `true` inside the delegate though... Are you saying you are seeing it as `false` inside the delegate? – Bojan Resnik Aug 09 '11 at 02:56
  • 1
    Nay, the problem appeared to be that I was calling `.Start()`, and the Mono implementation for WP7, did *not* warn me that it wasn't legal. (I tested with a .NET 4.0 console app, which told me it was wrong). – Claus Jørgensen Aug 09 '11 at 03:00
  • I made the same mistake as @Claus—I called `Start()` in a MonoTouch app and therefore the task finished before `TaskCompletionSource` had a chance to set its result. – Dan Abramov Nov 12 '12 at 14:25
1

This was a major pain point for me when learning the TPL as well.

What you're looking for is TaskCompletionSource. When you create a TaskCompletionSource, it creates a special Task object (accessible by the TaskCompletionSource.Task property) which only completes when you call the SetResult or SetException methods on the associated TaskCompletionSource.

This post explains how to wrap APM operations with the TPL (and also Rx). See also this gist demonstrating an APM operation wrapped in the TPL.

bvanderveen
  • 763
  • 11
  • 17