Managing asynchronous operations

Definition

Because an operation is stateless and handles a list of parameters, it is possible to launch it asynchronously. When this happens:

  • A temporary process executes the operation and receives the parameters.
  • The user who launches the operation can take control and continue to work in his/her session while monitoring the execution progress.
  • The temporary process ends automatically when the operation ends or if the server stops.

To allow the user to control the asynchronous running task, a set of APIs is available and described below.

The Context of execution

An operation is stateless. The code that is executed during the operation run has access to this, but the instance is empty. However, it can be filled using the AREAD or AINIT method. The parameters sent when calling the method are available as local variables.

Because this execution is asynchronous, the results need to be stored in the database, possibly in log files. When the execution ends, the instance is destroyed.

An asynchronous operation can run silently in the background, but it might be useful for a user to monitor the execution of this task. The user can view details in a dialog box that displays at the top of the page:

This dialog box opens automatically if the operation uses the appropriate API. Information messages can show the progress of the operation. The user can hide and reopen the dialog box by clicking the small icon highlighted in the red square.

An Abort link is also present, and an API allows the developer to check periodically if the execution has stopped.

When the operation is finished, the Abort link is replaced with a Download link. The user can download a file published by an API that could include a log file or any other results.

APIs available to control the execution

The following APIs, which are available to control the execution of asynchronous tasks, are all subprograms located in the same library.

Library ASYRASYNC

Call ASYNCTRACK

This call creates the dialog box if it doesn't already exist, and displays a message with the phase name and phase detail. A completion percentage can also be shown.

The parameters are the following:

Code Type and dimension Contents
CONTEXT ACTX instance Current context.
PAHASE_NAME Char Current phase to be dispayed.
PHASE_DETAIL Char Current detail to be displayed.
PERCENT Integer Progression percentage in the range 0-100.

Func ABORTREQUEST

This function checks if the user requests the task to be aborted. It returns '0' if no abort request was sent, and '1' otherwise.

The parameters are the following:

Code Type and dimension Contents
CONTEXT ACTX instance Current context.

Call ADDDIAGNOSE

This call adds a line in the text displayed in the bottom of the panel, in the diagnostics list.

The parameters are the following:

Code Type and dimension Contents
CONTEXT ACTX instance Current context.
DIAGNOSE_TEXT Char Diagnose text.
DIAGNOSE_STATUS Integer Status value as operation return them (can be CST_ASUCCESS, CST_AINFO or CST_AERROR).

Call LINKDOWNLOAD

This call adds a download link to a file that has been placed in a standard volume.

The parameters are the following:

Code Type and dimension Contents
CONTEXT ACTX instance Current context.
DATA_TYPE Char content type that describes the format of the downloadable file.
PATH Char The fila path on a standard volume (for instance, "[MYVOL]/myfile.tra", whre [MYVOL] has been defined in the "a href="../administration-reference/supervisor-administration-volumes.md">volume table.

Example

# This example is not based on the standard tables described in Sage X3.
# Let's imagine a link on the COUNTRY representation (class COUNTRY), that allows you  
# to trigger a method that posts all the invoices in an accounting system not yet accounted for 
# and addressed to customers who are located in the corresponding country.
# The current currency instance is CURRENCY.
# The parameter sent is the current country (called COUNTRY).
# The invoice (main table INVOICE) has a CUSTOMER reference column, a POSTED column (0 if not posted).
# The INVOICE class has a POST_IT method.
# Extract from script associated to the COUNTRY class:
$METHODS
Case AMETHOD
 When "POST"        : Gosub POST_COUNTRY
...
Endcase
Return
...
$POST_COUNTRY
# Miscellaneous declarations
Local File CUSTOMER [CUST]
Local File INVOICES [INV]
Local Instance CUR_INV using INVOICE
Local Instance MYLOG Using C_ALOG
Local Integer OK
Local Char LOGFILE_NAME(100)
# Let's generate the country class
  OK=Fmet this.AREAD([L]COUNTRY)
  If OK <> [V]CST_AOK : End : Endif
# Let's generate a class to generate the log
  MYLOG=NewInstance C_ALOG AllocGroup Null
# Open the log file
  OK=fmet MYLOG.ABEGINLOG("Invoice posting")
  LOGFILE_NAME=fmet MYLOG.AGETNAME
# How many invoices ?
Local Integer INV_COUNT, CUST_COUNT, INV_NUMBER
  Link [INV] with [CUST]CUSTKEY=[INV]CUSTOMER As [JOIN] Where [CUST]COUNTRY=this.COUNTRY and [INV]POSTED=0
& Order By Key CUSTDATE=[CUST]CUSTKEY;[INV]INVDATE
  INV_NUMBER=rowcount([JOIN])
# Let's perform a double loop on invoices
  For [JOIN]CUSTDATE(1)
    CUST_COUNT+=1
    For [JOIN]CUSTDATE
      # Let's update the asynchronous box for every invoice
      Call ASYNCTRACK(this.ACTX,
&                     "Managing customer"-[CUST]NAME,
&                     "Invoice"-[INV]INVNUM,
&                     int(100*(INV_COUNT/INV_TOTAL))) From ASYRASYNC
      # Let's call the invoices method to generate and post it
      CURINVOICE = NewInstance INVOICE AllocGroup null
      OK=fmet CURINVOICE.AREAD([INV]INVNUM)
      If OK<>[V]CST_AOK
        Call ADDDIAGNOSE(this.ACTX,"Error on invoice"-[INV]INVNUM-"reading",OK) From ASYRASYNC
      Else
        OK=Fmet CURINVOICE.POST_IT
        If OK=[V]CST_AOK
           OK=fmet MYLOG.APUTLINE("Invoice"-[INV]INVNUM-"successfully posted",OK)
        Endif
      Endif
      INV_COUNT+=1
      FreeInstance CURINVOICE
    Next
    # Check if the user wants to stop only when a complete customer has finished
    If func ASYRASYNC.ABORTREQUESTED(this.ACTX)
      Call ADDDIAGNOSE(this.ACTX,"Interruption. "-num$(INV_COUNT)-"invoices posted",[V]CST_AWARN) From ASYRASYNC
      Break
    Endif
  Next
  # Now the generation is complete
  OK=fmet MYLOG.APUTLINE(num$(INV_COUNT)-"invoices(s) successfully posted for"-num$(CUST_COUNT)-"customers",[V]CST_AOK)
  OK=fmet MYLOG.AENDLOG  
# Let's allow the log file (in [TRA] volume) to be downloaded 
  Call ADDLINKDOWNLOAD(this.ACTX,"ATYPE7","[TRA]/"+LOGFILE_NAME) From ASYRASYNC
  FreeGroup MYLOG
End

Note

Using these API is of course important if the operation is defined as Asynchronous in the link definition of the representation. But if the operation is described as synchronous, the previous calls will not cause an error: they will simply do nothing. This means that the design of the script associated with a task that might be called in asynchronous mode does not need to be tested if it is the case or not.