1

I have a class where each method execute asynchronously, i.e. return a Task, but where each method should nevertheless wait for the completion of the preceding call.

Continuation, right?

Except that a task continuation takes a delegate (Action) in parameter, not another task.

I've tried different things and the best I could do to make it work is the following (to me quite complex) code:

    private Task QueueTask(Func<Task> futureTask)
    {
        var completionSource = new TaskCompletionSource<int>();

        _lastTask.ContinueWith(async t =>
        {
            try
            {
                await futureTask();

                completionSource.SetResult(0);
            }
            catch (Exception ex)
            {
                completionSource.SetException(ex);
            }
        });

        _lastTask = completionSource.Task;

        return _lastTask;
    }

Here _lastTask is a private member of my class. Since all calls are coming from the UI thread, I just keep the last task and put continuation on it.

As I said I find this code quite convoluted. Do you have a better suggestion?

svick
  • 236,525
  • 50
  • 385
  • 514
  • It's not clear that what you've got actually does what you want it to anyway. What do your `Func` methods do? If they're creating their own tasks, are they also starting them? If you could provide a short but *complete* example, it would really help. – Jon Skeet Jul 09 '13 at 15:27
  • Also, have you seen the overloads for `ContinueWith` that accept a `Func>`? It seems like the problem is more that you're ignoring the existing task part, than the return value part... – Jon Skeet Jul 09 '13 at 15:28

1 Answers1

2

To me, it seems like you're asking the wrong question. A queue of tasks like this is an unusual requirement. We don't know anything about the actual problem you're trying to solve, so we can't suggest better approaches.

ContinueWith is intended for dynamic parallel processing, so it doesn't quite fit in with async code. However, you can use ContinueWith paired with Unwrap to sort-of emulate the behavior of await (if you ignore how await interacts with the current context).

So you can simplify your queue of tasks solution as such:

private Task QueueTask(Func<Task> futureTask)
{
    _lastTask = _lastTask.ContinueWith(t => futureTask()).Unwrap();
    return _lastTask;
}

However, there are probably better solutions. If the purpose of the queue is to provide exclusive access, a SemaphoreSlim would be more natural. If you actually do need a queue for some reason, consider using a Dataflow mesh.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810