How to create an asynchronous process in Business Central

Hello, this post is dedicated a bit to how to run asynchronous processes in Business Central through Page Background Tasks.

Running asynchronous processes here is very similar at the concept level than in other languages, only that we have some very important limitations that need to be mentioned.

The main idea is to be able to execute long-term calculations without hurting performance or crashing the page/application, running these processes through background tasks. This will make the page more responsive and load much faster.

How to create a page background task

Steps to create the asynchronous process:

  1. On the page where we will execute the process in the background, we must create and place the queue that executes the specified code.

This would be something similar:

CurrPage.EnqueueBackgroundTask(WaitTaskId, CodeunitId, TaskParameters, Timeout, ErrorLevel);

WaitTaskId
 Type: Integer
Specifies the ID of the new page background task. The ID is assigned to the TaskId variable after the task is queued successfully. This parameter is passed by reference to the method.

CodeunitId
 Type: Integer
Specifies the ID of the codeunit to run when the task is started.

TaskParameters: Dictionary of [Text, Text] ([Optional])
Specifies a collection of keys and values that are passed to the OnRun trigger of the codeunit that runs when the page background task session is started.

Timeout([Optional])
 Type: Integer
Specifies the number of milliseconds that the page background task can run before it is automatically cancelled.

ErrorLevel([Optional])
 Type: PageBackgroundTaskErrorLevel
Specifies the level of error handling on page background task level.

  1. We must create the codeunit that includes the logic that you want to run in the background.
  1. The process at the end will have 3 options.
  • That it executes correctly: In this case, we must notify the page that called the background task that the process finished, here handle the results of the background task and update the UI.

We must create the following Trigger:

trigger OnPageBackgroundTaskCompleted(TaskId: Integer; Results: Dictionary of [Text, Text])

TaskId

 Type: Integer
Specifies the ID of the background task that was run. The ID is automatically assigned to the background task when it is created.

Results

 Type: Dictionary[Text,Text]
Specifies the results of the page background task.
  • That it executes in error: similar to the previous case, we must notify the user or the page that invoked the process that the background task could not be completed and perhaps indicate the reason.

We must create the following Trigger:

trigger OnPageBackgroundTaskError(TaskId: Integer; ErrorCode: Text; ErrorText: Text; ErrorCallStack: Text; var IsHandled: Boolean)

TaskId

 Type: Integer
Specifies the ID of the background task that was run.

ErrorCode

 Type: Text
Specifies the severity level of the error that occurred.

ErrorText

 Type: Text
Specifies the message that explains the error that occurred.

ErrorCallStack

 Type: Text
Specifies the call stack for the error that occurred.

IsHandled

 Type: Boolean
true indicates that the error has been handled; false indicates that it has not been handled
  • That the task is canceled: this will happen if the page is closed before the task is completed.

The following image shows the background task execution flow.

Image taken from Docs.microsoft.com

EnqueueBackgroundTask

After a bit of theory, let’s do a practical example to see the benefits and how to run the Task in the background.

The first thing I did was modify the barcode project to include this new approach.

If you haven’t seen this project yet, here’s the link to the post.

As a first step we must invoke the EnqueueBackgroundTask method.

We can do this in the Onaftergetrecord or in the Oninit of the page on which the process will run in the background.

In this case I preferred to do it in OnAfterGetCurrRecord, as it was seen we only have our codeunit as a parameter that will run in the background the value of the bar code that we want to generate.

In this case I preferred to do it in OnAfterGetCurrRecord.

Let’s see the parameters used:

TaskParameters only has as record the value of the barcode that we want to generate.

Codeunit: PBTProcessWS is the name of the codeunit that will execute the call to the azure functions webservices to generate the barcode image.

Timeout: 10000 milliseconds. This value is optional, it is configured according to the business logic that we are writing, but if it is very low it will give an error in case the background process has not finished executing.

ErrorLevel: PageBackgroundTaskErrorLevel::Warning

Codeunit running in the background

As a second step we must create a codeunit that runs in the background.

Considerations that the codeunit must have:

  • It can’t display any UI such as dialog windows.
  • It can only read from the database.
  • The OnRun() trigger is invoked without a record.
  • Casting must be manually written in code by using Format() and Evaluate() methods.

As I mentioned before, here we can generate long-term calculations, generally, no matter how fast they run, they will always have a delay.

This codeunit is very similar to the examples given in the Microsoft portal, I have modified it slightly to make the call to the Azure Function that will return the barcode and additionally calculate the duration of the process.

In the next line we can see the call to the codeunit that makes the http request.

FileArrayBase64 := Helper.GetBarcodeFromAzure(Value);

The original barcode project has also been slightly modified to be able to obtain the response in Text and thus, at the end of the background process, to be able to send said response to the main page that invoked the method.

Below is the modified GetBarcodeFromAzure code.

Termination of the process in Background

As the third and last step, we must handle the 2 possible events that will be executed when the background process ends.

At this point we have 2 possibilities.

  1. Successfully complete:

In this case we must notify the page that invoked the process, the corresponding results, for this we have created the OnPageBackgroundTaskCompleted trigger as follows:

In the previous code we are taking the response from the Web Services, and we are converting it to Instream to then do the same import that we did before in “Rec.Barcode.ImportStream

Finally, we are taking the time that the process lasted and we are notifying the user.

  1. The process ends in error:

There is always the possibility of an error, maybe a timeout, the webservice is in some kind of error, etc.

In this scenario, we must be prepared to handle the corresponding error and make the update or notification to the page that makes the call to the background process.

For this case, we have used the following trigger:

Tests

Video 1:

Showing how the process runs in the background successfully

Video 2:

Timeout of the EnqueueBackgroundTask is modified with a time of 100ms. This will not give enough time to execute the process in the backgroud and will generate an error.

Video 3

Finally, I have modified the codeunit that makes the http request and I have placed an Error in it to see how the OnPageBackgroundTaskError trigger catches it

Design considerations and limitations

  • By default, only five page background tasks can run simultaneously for a main session. If there are more than five, they are queued and executed when space becomes available when other tasks finish.
  • It cannot display any user interface, such as dialog windows.
  • It can only be read from the database.
  • If the record id is changed, the task will be aborted.
  • On list pages, it is recommended that you do not queue a page background task from the OnAfterGetRecord trigger unless you are aware of the consequences, because the task will be canceled immediately after the first task is retrieved. row. Because the record changes for each row, the page’s background task is killed when the trigger runs after the first row.

Conclusion

Despite some of its limitations, the asynchronous programming approach in Business Central is very useful and powerful for creating well-performing extensions in the event that you need to run processes that could slow down or blocking user activities.

This approach could also be used to create auto refreshes if we put the ENQUEUEBACKGROUNDTASK method on either the OnPageBackgroundTaskCompleted or OnPageBackgroundTaskError triggers, depending on your scenario.

Finally I leave the links of the repositories used in Github.

Business Central.
Azure Functions.

I hope this will help.

Enjoy and share.

Leave a Reply

Your email address will not be published. Required fields are marked *