In AX7, we have various new ways to execute time consuming operations in the web client. We all know the feeling that if we want to execute a time consuming operation on an AX form, that operation blocks all the user interface until it is completed showing a white screen or waiting message. To prevent this, in AX 2012 we used either the progress bar to display the operation progress to user, or some asynchronous execution possibilities like the Thread API or xApplication::runasync() method to run the operation on the background.

In AX7 optimizing our time consuming operations is extra important because we are running them on a web based interface which will be blocked, or run into a timeout if we run such a long operation. For that reason, optimizing our execution time and leaving the user interface free for the user with asynchronous execution is important and recommended. We already see it is being used in many new forms of AX7, like the new data import form.

Now lets have a look at the different possibilities we have for running time consuming operations in AX7. To test them we create a simple form like this :

Capture1

Then we create a new class with a static method that holds a time consuming program code in it, in that example a 10 seconds of sleep :

class TestOperation
{
public static container returnAfteraWhile(container _callerparameters)
{
str caller = conPeek(_callerparameters, 1);

sleep(10000);

return(['Operation completed : ' + caller]);
}
}

First let’s look at the options we can execute our code synchronously. On the first button we call the static method directly without using anything else in the code, this displays the standard AX7 ‘please wait’ message to us and blocks all other user operations :

[Control("Button")]
class CallNormal
{
///
<summary>
///
/// </summary>

public void clicked()
{
super();

TestOperation::returnAfteraWhile(['Syncronous operation..']);
info('Syncronous operation finished.');
}

}

In our second button we write a code to run the same operation using the new SysOperationSandbox class of SysOperation framework, which replaces the old RunbaseBatch framework in AX 2012:

[Control("Button")]
class CallSysOperationSandbox
{
///
<summary>
///
/// </summary>

public void clicked()
{
super();

SysOperationSandbox::callStaticMethod(classNum(TestOperation),staticMethodStr(TestOperation,returnAfteraWhile),
['Sandbox Run Sync'], 'Waiting for sandbox operation', 'Operation completed');

}

}

This will run the same code displaying user a progress window, also blocking the other parts of the user interface :

Capture3

To run our program code asynchronously in the background without blocking the user interface, we use the new runasync methods of FormRun and Global classes. In AX2012 for that purpose we have used the xApplication::runasync() method and Thread framework. Those two are now depreciated and replaced by the optimized Form and Global class methods, which utilizes .NET Tasks for multi-threaded execution in AX.

For the ones who are not familiar with asynchronous programming, our asynchronous method call will simply run in the background without interfering with the user interface and run another method called the ‘callback method’ when it is finished. We create our callback method in the root declaration of our form like this :

public void FormCallBackTest(AsyncTaskResult _result)
{
container result, resultinfolog;
str resultstr;
;

info('Async operation finished.');
result = _result.getResult();
resultstr = conPeek(result, 1);

info(resultstr);

resultinfolog = _result.getInfologData();

if(resultinfolog != conNull())
{
setPrefix('Extra information');
InfoLogExtension::showMessagesFromContainer(resultinfolog);
}

}

And call the same method in our class from our third button using new Formrun runasync method :

[Control("Button")]
class CallElementRunAsync
{
///
<summary>
///
/// </summary>

public void clicked()
{
super();

info('process is running..');
element.runAsync(classNum(TestOperation), staticMethodStr(TestOperation,returnAfteraWhile),['Element Run Async'],
System.Threading.CancellationToken::None, formMethodStr(AsyncTestForm,FormCallBackTest));
}

}

This code will run the static class method on the background and call the callback function passing an AsyncTaskResult object to the method. From that object we can check the return value of the async method call (must be a container) and get the infolog result of the async execution.

You can click other buttons in the form and do other things as well without waiting for the code execution to complete, and we receive information messages as soon as the execution is finished.

The runAsync method of formRun is the one optimized for client side execution. There is another runAsync method on Global class which is to be used in program code that runs outside the client, like services. In our fourth button we will use that one instead of Formrun one (for testing purposes, always use formrun one in client side) and will see the difference.

For that, we need to create a callback function in our class. Just simply copy and paste the callback method from the form and change it to run as static and inside the class instead :

public static void ClassCallBackTest(AsyncTaskResult _result)
{
container result, resultinfolog;
str resultstr;
;

info('Async operation finished.');
result = _result.getResult();
resultstr = conPeek(result, 1);

info(resultstr);

resultinfolog = _result.getInfologData();

if(resultinfolog != conNull())
{
setPrefix('Extra information');
InfoLogExtension::showMessagesFromContainer(resultinfolog);
}

}

And in our button we call similar function for Global class :

 [Control("Button")]
class CallGlobalRunAsync
{
///
<summary>
///
/// </summary>

public void clicked()
{

super();

info('process is running..');
Global::runAsync(classNum(TestOperation), staticMethodStr(TestOperation,returnAfteraWhile), ['Global Run Async'],
System.Threading.CancellationToken::None, classNum(TestOperation), staticMethodStr(TestOperation,ClassCallBackTest));

}
}

After you run the code you will realize that you do not receive the information message in time although the operation is completed. This is because the asynchronous execution and callback is done outside the form and form handler does not know about it. You need to refresh the form to see the result of the execution in messages after it is finished. In Formrun version you do not need to do it and get the messages as soon as they are placed in the infolog.

You can download the program code used in this example from my GitHub link below :

AsyncOperations.zip

 

8 thoughts on “Synchronous and asynchronous operations in AX 7

  1. I want to display progress status when batch process is going. (exp 20% done…) Is there any way to do with SysOperationSandbox?

  2. How we can lock some objects in async operation in AX7, I mean when we have some operation which inserts data to list, so we have to lock inserting operations, but when I try to use this piece of code:

    try
    {
    System.Threading.Monitor::Enter(locker);
    list.addEnd(data);
    }
    finally
    {
    System.Threading.Monitor::Exit(locker);
    }

    I always have empty list

    1. It is not very clear what you want to achieve from the code but Enter and exit methods take System.Object type as first parameter. They may not accept an AX object as a parameter and throw an exception. Then it falls into finally and list.addEnd is not executed.

  3. Hi Tayfun Sertan Yaman,
    I understood the concept explained in the above blog, Can you please explain, what is the difference between the Sysoperation framework Async and Normal Asynchronization(Above example). Which will be best approach to use?
    Regards,
    Nagesha

    1. The Async running is a feature of AX operation service controller class to run AX services asynchronously. The feature even exists in AX 2012.
      With above examples you can run your code directly async without a operation controller class and service.
      Behind the scenes SYSOP framework uses the same async methods of the ‘Global’ class:

      Class : SysOperationFrameworkService

      internal static void runAsyncObject(ClassId _runClassId, MethodName _runInstanceMethod, Object _callbackObject, str _callbackMethodName, container _parameters)
      {
      xGlobal::runAsyncWithObjectCallback(
      classNum(SysOperationFrameworkService),
      staticMethodStr(SysOperationFrameworkService, instanceToStaticMethodCallWrapper),
      [_runClassId, _runInstanceMethod, _parameters],
      _callbackObject,
      _callbackMethodName);
      }

      1. Hi Tayfun Sertan Yaman,
        Thanks for details information.Currently i am working on AX2012 Asynchronization process,Do you have any sample code?. I tried by using xApplication::runasync() but i am facing some issues. Please share if you have sample code or standard class, So i can refer
        Regards,
        Nagesha

Leave a Reply