If I write a simple function I can get a result immediately. If I use async/await
and return a Task
- the method will kinda return when it's done with the task, but what if I need to write a method that needs to return immediately, but then continue updating the result and potentially eventually complete the task? Also what if I want to expose it outside a WinRT Component library for consumption in components in other languages? How would I do it in C# and how would I do it in C++/CX? Or JS perhaps?
Example 1:
I want to expose a property that returns an ImageSource
so I can immediately bind it from my MVVM view model to the XAML view. The method to load the ImageSource
is going to be in a separate class that is exposed outside a WinRT Component (it is a public method). Now I want that method to be awaitable, or at least somehow return a task I can await but also immediately return the ImageSource
so the property I call it from can immediately return since properties can't be async. The caller doesn't know what type the ImageSource
will be so it cannot instantiate it since ImageSource
is virtually an abstract type and is typically represented as a BitmapImage
or WriteableBitmap
and in my case both can be returned from the method. Obviously the method itself immediately knows whether it is going to return either of the types of objects, but it takes some time to read/create and or decode the image.
I'm thinking the signature might be something like this in C#
public async Task<ImageSource> GetImage(
object key,
out ImageSource bitmap,
CancellationToken cancellationToken)
and I would simply not await the result of the method in the property accessor, but I'm thinking I'll be able to return the bitmap argument immediately, while when called elsewhere or event elsewhere in the code of my view model I'll be able to await or cancel the task.
Example 2:
I want to be able to list files from a disk and get a task that is complete when all files have been listed, but immediately return an IObservableVector of view models representing the files for use in my XAML UI that updates as pages of the files are loaded asynchronously.
Here I would probably do something similar:
public async Task<int> GetImages(
object queryParemeters,
out ObservableCollection<CustomFileInfoType> files,
CancellationToken cancellationToken)
Problems
Now the above look almost good, but I don't think I can expose a TPL Task outside a WinRT Component since Task
is not a WinRT type, so I'd probably gave an internal method like the above and a public one that wraps the result as an IAsyncOperation
by calling AsyncInfo.Run()
, passing the task and cancellation token. ObservableCollection
is also .NET only, so I'd probably need to create a wrapper around it that implements an IObservableVector
since I don't think one is available in .NET. There are probably other potential problems with those and I am not sure if this design is right.
Then also - how would I do all this in C++/CX? Or JS?
async
is built on the notion of an asynchronous operation, with a definite beginning and ending. At the end, there may be a single result. That's it. Note that async
methods may not have out
parameters because they don't fit into this model.
If you want a stream of values, then use Reactive Extensions. There's an interesting RxUI library that nicely marries observables with MVVM patterns.
That said, I don't think either of your examples actually require observables (though you certainly could move to Rx if you wanted). I address your first example (data-bound async
properties) on my blog; the short answer is to use a wrapper for Task<T>
that implements INotifyPropertyChanged
like this one:
// Service
public async Task<ImageSource> GetImage(object key, CancellationToken cancellationToken);
// ViewModel
INotifyTaskCompletion<ImageSource> Image { get; private set; }
...
Image = NotifyTaskCompletion.Create(GetImage(key, token));
// View
<Image Source="{Binding Image.Result}" />
Regarding your second example, this can be done fairly easily by treating the new items as progress updates from the async
method:
// Service
public async Task<int> GetImages(object queryParemeters,
CancellationToken cancellationToken,
IProgress<CustomFileInfoType> progress);
// ViewModel
var collection = new ObservableCollection<CustomFileInfoType>();
var progress = new Progress<CustomFileInfoType>(x => collection.Add(x));
await GetImages(query, token, progress);
Exposing these types is something else completely. WinRT components must expose WinRT types. I recommend you write the basic logic (service and possibly ViewModel) using pure async
/await
and then do the translation separately. As you noted, AsyncInfo.Run
will translate Task
to IAsyncOperation
, and there isn't a built-in translator for ObservableCollection
to IObservableVector
(though it isn't hard to write, and there are several available via Google).
Then also - how would I do all this in C++/CX? Or JS?
I have no idea on that one. You'll probably have to write your own equivalent for NotifyTaskCompletion
on those platforms, or just use callbacks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With