Async with value as callback parameter.

I’ve spent the day working through Async.h implementing task management for the various design patterns I use in my C# apps. It’s been a C++ slog but I finally have compiling & funcitonal boilerplate for each use case except for one.

Specifically, How do i go about ensuring the completion callback contains the result of the Future’s Get method without resorting to a class level variable for the result of the Async call?

The below is what I’m thinking, but naturally result_E.Get() isn’t valid in it’s current location

void FWortWortWort::DoWork(){
	TFunction<FString()> task_E = [this]() {return this->Async_TestE(); };

	TFuture<FString> result_E;
	
	TUniqueFunction<void()> callback = [this, strYarp = result_E.Get()]() { OnTask_E_Completed(strYarp); };
	
	result_E = Async(EAsyncExecution::ThreadPool, task_E, MoveTemp(callback));

	UE_LOG(LogVTSFileManager, Log, TEXT("Some Other Work"));
}

FString FWortWortWort::Async_TestE() {
	UE_LOG(LogVTSFileManager, Log, TEXT("Start Async E"));

	FPlatformProcess::Sleep(2);//TODO: Solve the 3 body problem

	UE_LOG(LogVTSFileManager, Log, TEXT("End Async E"));

	return TEXT("Return Async E");
}

void FWortWortWort::OnTask_E_Completed(FString strValue) {
	UE_LOG(LogVTSFileManager, Log, TEXT("Async E Completed:  %s"), *strValue);
}

Hi there, you’re not far off in your approach however you need to correctly capture the result after the async task has finished. The main issue in your code is that you’re trying to capture the result of result_E.Get() before the future has been resolved, which isn’t valid.

Something along these lines:

void FWortWortWort::DoWork() {
    // Define the task to be executed asynchronously.
    TFunction<FString()> task_E = [this]() { return this->Async_TestE(); };

    // Create a promise to set the result and a future to get the result.
    TPromise<FString> promise_E;
    TFuture<FString> result_E = promise_E.GetFuture();

    // Define the completion callback to handle the result.
    TUniqueFunction<void()> callback = [this, result_E = MoveTemp(result_E)]() mutable {
        FString strYarp = result_E.Get();
        OnTask_E_Completed(strYarp);
    };

    // Run the task asynchronously and fulfill the promise when done.
    Async(EAsyncExecution::ThreadPool, [promise_E = MoveTemp(promise_E), task_E = MoveTemp(task_E), callback = MoveTemp(callback)]() mutable {
        FString result = task_E();
        promise_E.SetValue(result); // Set the result.
        callback(); // Invoke the callback with the result.
    });

    UE_LOG(LogVTSFileManager, Log, TEXT("Some Other Work"));
}

FString FWortWortWort::Async_TestE() {
    UE_LOG(LogVTSFileManager, Log, TEXT("Start Async E"));

    FPlatformProcess::Sleep(2); // Simulate long computation.

    UE_LOG(LogVTSFileManager, Log, TEXT("End Async E"));

    return TEXT("Return Async E");
}

void FWortWortWort::OnTask_E_Completed(FString strValue) {
    UE_LOG(LogVTSFileManager, Log, TEXT("Async E Completed: %s"), *strValue);
}

Hope this helps.

I knew I was close, but was missing some complexity pertaining to the referencing of the future.

I Didn’t think about using a promise to hold the future but passing the promise instead of the future worked as desired, Thank you.

Async requires a lot of boilerplate to use, I’ll see if I can turn it into a series of macros to make life easier.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.