22. September 2019
Asynchronous programming in D365 Business Central 2019 wave 2: Page Background Tasks
In a lot of other programming languages, the concepts of asynchronous calls and background tasks are well established and supported. The idea is to have the ability to kick of something in the background without having to wait for the result and thereby blocking the user. Instead the user can just keep working on something else. It also helps if you want to quickly show a form without delay even if some calculation has to be made for some parts of it. Imagine immediately getting a start page or form and then something in the background gets data to display additionally like a share price or weather forecast which might take a bit to fetch. While Dynamics NAV / BC has support for something like that to a degree, the 2019 Release Wave 2 of Business Central expands that support a lot, allowing you to tackle many of those scenarios quite easily.
With a new concept called “Page Background Tasks” you can create child sessions in the background which execute functionality in codeunits. Once the background task is finished, the parent session can get the results and work with them like show it in a field, update a diagram or show a notification. The implementation is done by calling
EnqueueBackgroundTask to kick off a Codeunit with the background task and then handle the results in a trigger called
OnPageBackgroundTaskError if an error happens. For the user, the main benefit is a very quickly starting page where the information generated in the background appears as soon as it is available. As an example, see the following implementation where the user opens the customer card and then loads share price information. The page loads immediately with the already available data – in that case the share name – and the price information appears as soon as the request to an external WebService returns.
Implementation details and the example explaimed:
As mentioned above, the implementation consists of four parts:
- Five calls to
EnqueueBackgroundTask happen in the
OnAfterGetCurrRecord trigger to start the background tasks. As parameters
EnqueueBackgroundTask takes a task ID to later correlate the result to the call, the ID of the Codeunit to call, parameters for the background task as Dictionary, a timeout in milliseconds and the criticality level if an error appears. The task ID needs to be a global integer which will be set automatically when the task is enqueued.
CurrPage.EnqueueBackgroundTask(TaskPriceId, 50100, TaskParameters, 20000, PageBackgroundTaskErrorLevel::Warning);
- A codeunit to do what needs to be done in the background, in the example call a WebService to get the share price. I have used a service called World Trading Data, a free service offering you access to that information. In order to make the example work, you need a free API key, which you can get by just creating an account there. It returns some JSON, so in the Codeunit I am calling the service and parse the results:
codeunit 50100 PBTGetSharePrice
TableNo = Customer;
Result: Dictionary of [Text, Text];
ShareId := Page.GetBackgroundParameters().Get('ShareID');
ElementKey := Page.GetBackgroundParameters().Get('Element');
if (ShareId <> '') then begin
WebServiceKey := '<add api token here>';
BaseURI := 'https://api.worldtradingdata.com/api/v1/stock?api_token=' + WebServiceKey;
HttpClient.Get(BaseURI + '&symbol=' + ShareId, HttpResponseMessage);
if (not HttpResponseMessage.IsSuccessStatusCode) then
Error('Couldn''t retrieve the share data');
ElementValue := ValueToken.AsValue().AsText();
Sleep((Random(5)) * 1000);
As you can see, first the parameters are read from the parameter Dictionary (lines 20 and 21) using
Page.GetBackgroundParameters() and the WebService is called. Then the result is parsed and the requested value is added to the result Dictionary (lines 39 and 40) using
Page.SetBackgroundTaskResult(). For the demo case, I also added a random sleep between one and five seconds (line 36), so that the results come back with a humanly visible delay because the WebService is so fast that without the sleep it seems instantaneous. Of course in a real implementation you wouldn’t include that sleep.
- To work with the result, I have implemented the trigger
OnPageBackgroundTaskCompleted to check which task has finished using the task ID variable and then just set the right variable which I show on the page:
page 50100 "Customer Share Details"
Caption = 'Share Details';
ApplicationArea = All;
Caption = 'Share name';
ApplicationArea = All;
Caption = 'Current price';
trigger OnPageBackgroundTaskCompleted(TaskId: Integer; Results: Dictionary of [Text, Text])
if (TaskId = TaskPriceId) then begin
PriceVar := Results.Get(PriceKey);
- For error handling, there is also a default mechanism in place, but to show how you can adapt that I have also added an error handling trigger
OnPageBackgroundTaskError which works a bit differently if a problem occurs when parsing the result JSON. It “catches” the original error, marks it as handled so that the default mechanism doesn’t handle it and instead throws an error with a slightly more user friendly message:
trigger OnPageBackgroundTaskError(TaskId: Integer; ErrorCode: Text; ErrorText: Text; ErrorCallStack: Text; var IsHandled: Boolean)
if (ErrorCode = 'JsonPropertyNotFound') then begin
IsHandled := true;
Error('Couldn''t find data for %1', ShareID);
If you want to give this a try, you can clone my repository for this example on Github. Don’t forget to get your own API key for the WebService and place it in line 194 of the .al file. You will also need to add your own data in the launch.json file in folder .vscode. After installing the extension, open a customer card and add a share ID like MSFT or GOOG. Then execute action “Share Info” below “Customer” and you should see the page background tasks in action!
If you want to learn more about this feature and get a deep dive, this session at Directions EMEA by Damien Girard, Jens Moller-Pedersen and Kennie Pontoppidan will cover all the details.