Search code examples
c#asynchronouswindows-phone-7.1task-parallel-libraryasync-ctp

Multiple Task<T> or IProgress<T> for partial completion?


I am using an asynchronous operation that completes in three steps, returning partial results before completion. Originally it's using events to signal progress, and I want to wrap it properly as an awaitable operation (or a chain of them).

To be exact, it's the PhotoCamera.CaptureImage method in Windows Phone 7, which fires events in the following order (with relevant result types):

  1. CaptureStarted, not that interesting to me
  2. CaptureThumbnailAvailable (Stream)
  3. CaptureImageAvailable (Stream)
  4. CaptureCompleted (bool)

How to wrap this? Currently I'm using a TaskCompletionSource and calling SetResult on CaptureCompleted. It does not address steps 2 and 3, the thumbnail and image becoming available. I decided to use callbacks for those instead.

My extension method (leaving out the code for brevity):

public static Task<bool> CaptureImageAsync(this PhotoCamera photoCamera,
                                           Action<Stream> thumbnailAvailableCallback,
                                           Action<Stream> imageAvailableCallback)

This doesn't feel entirely clean, a mixture of Tasks and callbacks. I know of IProgress<T>. However, the progress values aren't of uniform type (Stream, Stream, bool)... and wouldn't that stretch the IProgress<T> pattern too far?

Using multiple Task<T> that are awaited in order is an option, but that supposes a specific order that I'd also have to enforce somehow. Maybe each task result could contain the "next" task as a member:

ThumbnailResult thumbnailResult = await photoCamera.CaptureImageAsync();
...
ImageResult imageResult = await thumbnailResult.NextTask;
...
bool finalResult = await imageResult.NextTask;

I still think that I'm somewhat off the mark here. Ideas?


Solution

  • Personally i'll create a class for it with 3 methods returning Task<T>. The caller will be able to call the 3 methods in any order he wants and it'll always work. if GetFinalResultAsync is called first and then GetThumbnailAsync then only consequence is that the second Task<T> will aready have a result when await is used and it will return synchronously.

    var photoCapture = photoCamera.BeginCapture();
    
    var thumbnailResult = await photoCapture.GetThumbnailAsync();
    var imageResult = await photoCapture.GetImageAsync();
    var finalResult = await photoCapture.GetFinalResultAsync();
    

    To wait for everything :

    var thumbnailTask = photoCapture.GetThumbnailAsync();
    var imageTask = photoCapture.GetImageAsync();
    var finalTask = photoCapture.GetFinalResultAsync();
    
    await Task.WhenAll(thumbnailTask, imageTask, finalTask);
    DoWork(thumbnailTask.Result, imageTask.Result, finalTask.Result);