create_task and return values

17,688

Solution 1

How to make the method return after the async task has completed, so I can return a meaningful result based on the stuff going on asynchronously?

This doesn't make much sense: the "stuff" isn't asynchronous if you want to wait for it to complete before returning. That's the definition of synchronous.

When using C++/CX, you cannot wait on a not-yet-completed task on an STA. Any attempt to do so will result in an exception being thrown. If you are going to call Purchase() on an STA and if it starts an asynchronous operation, you cannot wait for that operation to complete before returning.

Instead, you can use .then to perform another operation when the asynchronous operation completes. If the continuation needs to be performed on the invoking thread, make sure to pass the use_current() continuation context to ensure that the continuation is executed in the correct context.

Solution 2

Sascha,

  1. Returning a task would abstract out the store, and I think that would be the most reasonable decision, since you are not restricting the users of your helper class to get the results straight away, but also allowing them to handle the results in their own way and asynchronously.
  2. As @James correctly mentioned, you are not allowed to wait in the UI thread, then you will make the app unresponsive, there are different ways to avoid waiting:

    • create a continuation with concurrency::task::then;
    • you cannot wait in the UI thread, but you can wait for an operation on the UI thread to complete, that means you can wrap the future result of the task running on UI in a task_completion_event and then wait on the event in another (background) thread and handle the result;

      concurrency::task_completion_event<Platform::String^> purchaseCompleted;
      
      create_task(CurrentAppSimulator::RequestProductPurchaseAsync(
          this->_LastProductId, false)).then(
              [purchaseCompleted](concurrency::task<Platform::String^> task)
      {
          try
          {
              purchaseCompleted.set(task.get());
          }
          catch(Platform::Exception^ exception)
          {
              purchaseCompleted.set_exception(exception);
          }
      });
      
      // and somewhere on non-UI thread you can do
      Platform::String^ purchaseResult = create_task(purchaseCompleted).get();
      
    • you can achieve the previous trick using more WinRT-specific facilities rather than Concurrency Runtime, more precisely, IAsyncOperation<T>::Completed and IAsyncOperation<T>::GetResults;

  3. and

  4. seem irrelevant here, since you have only 1 real task, which is make a purchase.
Share:
17,688
Samyeak Maharjan
Author by

Samyeak Maharjan

Developing at Infront Quant AG

Updated on June 22, 2022

Comments

  • Samyeak Maharjan
    Samyeak Maharjan almost 2 years

    I need to call an Async method within a method I declared. The method should return a value. I'm trying to wrap calls to the Windows Store into an easy to use class. My method should look like this:

    bool Purchase(enum_InAppOption optionToPurchase);
    

    enum_InAppOption is an enum consisting of all In-App options to purchase. At some point I need to call RequestProductPurchaseAsync. The result of this call determines if the method should return trueor false. I'm new to c++/cx (or at least I have a long history between now and the last time I used c++), so maybe this is easier as I think.

    The create_task looks like this:

    create_task(CurrentAppSimulator::RequestProductPurchaseAsync(this->_LastProductId, false))
    

    The options I considered / tried:

    1. returning the task would not abstract the store

    2. tried to call wait on the task. I've got the exception An invalid parameter was passed to a function that considers invalid parameters fatal.

    3. tried to use structured_task_group but it seems this does not allow for non void returning methods or I'm trying to provide a wrong interpretation. Compiler returns error C2064 (have googled but I can't get the point what to change)

    4. Using an array of tasks and when_all

    Found the following code on http://msdn.microsoft.com/en-us/library/dd492427.aspx#when_all in the middle of the page:

    array<task<void>, 3> tasks = 
    {
        create_task([] { wcout << L"Hello from taskA." << endl; }),
        create_task([] { wcout << L"Hello from taskB." << endl; }),
        create_task([] { wcout << L"Hello from taskC." << endl; })
    };
    
    auto joinTask = when_all(begin(tasks), end(tasks));
    
    // Print a message from the joining thread.
    wcout << L"Hello from the joining thread." << endl;
    
    // Wait for the tasks to finish.
    joinTask.wait();
    

    So I tried to translate it into the following code:

    array<task<Platform::String^>,1> tasks = {
        create_task(CurrentAppSimulator::RequestProductPurchaseAsync(this->_LastProductId, false))
    };
    

    Even though I included the compiler throws C2065 ('array': undeclared identifier), C2275 ('Concurrency::task<_ReturnType>': illegal use of this type as an expression and some errors that seem to be errors following up on those two.

    To sum up: How to make the method return after the async task has completed, so I can return a meaningful result based on the stuff going on asynchronously?