Unreal Engine - AsyncTask, Promises, Futures, and Responsive UI
Keeping the user interface smooth
Introduction
Unreal Engine provides a few mechanisms to run asynchronous code. AsyncTask wraps functionality of the TaskGraph in a simple and convenient function that runs code in the form of a function or lambda on a specified thread. Promises and Futures, while not inherently multi-threaded, provide capabilities to trigger a continuation function once they are marked as completed in a thread safe manner. Combining AsyncTasks with Promises and Futures can keep the UI responsive even when doing expensive operations.
Example
Below is a simplified example of a ViewModel that simulates an expensive task, perhaps reading a file or requesting data from a backend service, by sleeping the thread for 3 seconds before increasing the RunCount by 1.


This ViewModel is hooked up to the following widget using the View Bindings window. The bindings are:
Clicking on the Run Button calls the function
The Common Text Box holds the FText representation of RunCount.
There’s also an animated spinner to demonstrate when the game thread is blocked.
The following video demonstrates this ViewModel in action
Making it Asynchronous
This is the asynchronous version of the same ViewModel from above


Understanding TFuture and TPromise
At a high level, Futures provide the capability to call a function via the Next method after it has become fulfilled. This is convenient, because the function passed to Next will be called even in the case where the Future was fulfilled before Next was invoked.
Promises wrap a Future and provide the SetValue method to fulfill its internal Future.
Understanding the Threaded ViewModel
When DoALongRunningAsyncTask is called, the following happens:
The Task is marked as running immediately so the UI can respond accordingly
DoALongRunningAsyncTaskInternal is called
A SharedRef (non nullable version of a SharedPtr) of a Promise is created
This Promise is passed to an AsyncTask call via a lambda which will now hold a reference which will prevent it from falling out of scope and being deleted
The AsyncTask will start a thread sometime in the future that will:
Sleep for 3 seconds
Fulfill the Future of the Promise that was passed in by incrementing the RunCount by 1
The Promise’s Future is returned immediately
Next is called on the returned Future and a continuation function is passed in for whenever the Future is fulfilled. This continuation function passes a lambda to be called back on the Game Thread that will:
Mark the Task as completed by setting bIsTaskRunning to false
Increment the RunCount by the Result from the Future
The threaded ViewModel is hooked up to the following UI. This widgets adds bindings for an animated throbber that is visible when the task is running and disabling the Run button when the task is running.
The following video demonstrates the threaded ViewModel in action
Conclusion
AsyncTask, Promises, and Futures are a subset of tools that Unreal Engine provides to run asynchronous code. Interacting with the TaskGraph directly and the Task System are others. Expensive calls, such as interacting with a backend service or doing File I/O can be done on a thread other than the Game Thread to keep the UI and simulation smooth
Example code can be found here.
Example assets can be found here.