You're real close. A key point you might be missing is that then
returns you a new task. So the last then
in the chain determines the type of your task.
auto t = task<int>([] { return 0; });
// t is task<int>
auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; });
// t is task<double>
auto t = task<int>([] { return 0; })
.then([](int i) { return 3.14; })
.then([](double d) { return "foo"; });
// t is task<const char*>
If you just glance at the first line, it looks like you've always got a task<int>
, but as you can see that's not necessarily the case if you immediately call then
on it.
Secondly, keep in mind that your function is returning a task
, not the stream itself.
Usually you'd have the last then
in your chain return you the task that you'll return from your function, and rather than store the task in a local variable, you just return it. For example:
task<const char*>
get_foo()
{
return task<int>([] { return 0; })
.then([](int i) { return 3.14; })
.then([](double d) { return "foo"; });
}
Again, it looks a tad strange because a quick glance makes you think that you're returning a task<int>
. This is nicely handled by using create_task
rather than calling the task constructor explicitly. It frees you from having to explicitly specify the task's type at all. Additionally, it's easily changed to create_async
if you instead want to return an IAsyncInfo
derivative.
I'm not at all familiar with BitmapEncoder
, but here's a tweaked version of your code that might do the trick:
Concurrency::task<IRandomAccessStream^> GetImageStream()
{
auto stream = ref new InMemoryRandomAccessStream();
return create_task(BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, stream))
.then([this, width, height, imageBytes](BitmapEncoder^ encoder)
{
// are width, height, and imageBytes member variables?
// if so, you should only need to capture them OR "this", not both
encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 96.0, 96.0, imageBytes);
return encoder->FlushAsync();
}).then([stream]()
{
// this should work fine since "stream" is a ref-counted variable
// this lambda will keep a reference alive until it uses it
return stream;
});
}
The only real change is using create_task
and immediately returning its result.
I'm still learning PPL myself, but one thing I've learned that's held up so far is that you should pretty much always be doing something with any task you create. The usual thing to do is use then
to turn it into a new/different task, but you still need to do something with the task returned by the last then
. Oftentimes you just return it, as above. Sometimes you'll add it to a container of tasks which are then grouped together with when_all
or when_any
. Sometimes you'll just call get()
on it yourself to wait for its result. But the point is that if you create a task and don't do anything with it, then there's probably something wrong.