In this article I illustrate how to set input parameters and how to receive output values with a BackgroundWorker in Personalized Scripts for Lawson Smart Office. This article is a first in a set of articles about BackgroundWorkers to ensure code quality and usability.
As a reminder, time-consuming operations like M3 API calls, Web Service calls, or HTTP Requests, should be placed in a background thread to not freeze the user interface and to not result in bad usability. Refer to Karin’s post about Calling M3 APIs in JScript on a Background Thread.
Knowing how to set input parameters and how to receive output values with a BackgroundWorker will exempt us from using global variables; global variables in a multithreaded program are not thread safe because of possible race conditions that are hard to troubleshoot.
Counter-example
Suppose we have the following simple script that calls a time-consuming operation not in a BackgroundWorker; the time-consuming operation is in the UI thread and will freeze the user interface:
import System.Windows; package MForms.JScript { class Test { public function Init(element: Object, args: Object, controller: Object, debug: Object) { var inputParameters = ...; var outputValues = doSomethingTimeConsuming(inputParameters); } } }
BackgroundWorker
A BackgroundWorker executes code in a background thread without blocking the UI thread. There are many more lines of code involved: a BackgroundWorker is started with RunWorkerAsync(), the event handler OnDoWork runs in the background thread with the time-consuming operation, the event handler OnDunWorkerCompleted resumes in the UI thread, and we cleanup the worker after use. That advantage of BackgroundWorkers is that the UI doesn’t freeze, but the disadvantage is that the code must be carefully crafted and it’s not obvious.
Here’s a simple script that calls a time-consuming operation in a BackgroundWorker; as the time-consuming operation is in the background thread it will not freeze the user interface:
import System.ComponentModel; package MForms.JScript { class Test { /* UI thread */ public function Init(element: Object, args: Object, controller: Object, debug: Object) { var worker = new BackgroundWorker(); worker.add_DoWork(OnDoWork); worker.add_RunWorkerCompleted(OnRunWorkerCompleted); worker.RunWorkerAsync(); } /* background thread */ function OnDoWork(sender: Object, e: DoWorkEventArgs) { doSomethingTimeConsuming(); } /* UI thread */ function OnRunWorkerCompleted(sender: Object, e: RunWorkerCompletedEventArgs) { var worker: BackgroundWorker = sender; worker.remove_DoWork(OnDoWork); worker.remove_RunWorkerCompleted(OnRunWorkerCompleted); } /* something time-consuming */ function doSomethingTimeConsuming() { System.Threading.Thread.Sleep(1000); } } }
How to set input parameters
To pass input parameters to a BackgroundWorker we don’t have to use global variables; we can set the input values as the argument of RunWorkerAsync and get e.Argument in OnDoWork:
public function Init(element: Object, args: Object, controller: Object, debug: Object) { ... var inputParameters = ...; worker.RunWorkerAsync(inputParameters); } function OnDoWork(sender: Object, e: DoWorkEventArgs) { var inputParameters = e.Argument; ... }
How to receive output values
To get output values from a BackgroundWorker we don’t have to use global variables either; we can set e.Result in OnDoWork and get e.Result in OnRunWorkerCompleted:
function OnDoWork(sender: Object, e: DoWorkEventArgs) { ... var outputValues = ...; e.Result = outputValues; } function OnRunWorkerCompleted(sender: Object, e: RunWorkerCompletedEventArgs) { var outputValues = e.Result; ... }
Final source code
Here is the final source code illustrating how to create a BackgroundWorker, do the time-consuming operation in the background thread, set input parameters and get output values (I use object literals), cleanup the worker, and not use global variables.
import System.ComponentModel; package MForms.JScript { class Test { /* UI thread */ public function Init(element: Object, args: Object, controller: Object, debug: Object) { // set input parameters for the background thread var inputParameters = { 'CONO': 100, 'CUNO': 'ACME' }; // create and start worker var worker = new BackgroundWorker(); worker.add_DoWork(OnDoWork); worker.add_RunWorkerCompleted(OnRunWorkerCompleted); worker.RunWorkerAsync(inputParameters); } /* background thread */ function OnDoWork(sender: Object, e: DoWorkEventArgs) { // get input parameters from the UI thread var inputParameters = e.Argument; var CONO = inputParameters.CONO; var CUNO = inputParameters.CUNO; // do something time-consuming doSomethingTimeConsuming(); // return output values to the UI thread var outputValues = { 'CUNM': 'A Company Manufacturing Everything', 'CUA1': '123 Main St.', 'CUA2': 'SAN FRANCISCO, CA' }; e.Result = outputValues; } /* UI thread */ function OnRunWorkerCompleted(sender: Object, e: RunWorkerCompletedEventArgs) { // get output values from the background thread var outputValues = e.Result; var CUNM = outputValues.CUNM; var CUA1 = outputValues.CUA1; var CUA2 = outputValues.CUA2; // cleanup worker var worker: BackgroundWorker = sender; worker.remove_DoWork(OnDoWork); worker.remove_RunWorkerCompleted(OnRunWorkerCompleted); } function doSomethingTimeConsuming() { System.Threading.Thread.Sleep(1000); } } }
Conclusion
In this article I illustrated how to set input parameters and how to receive output values with a BackgroundWorker in Personalized Scripts for Lawson Smart Office. Knowing these techniques is important for code quality, but the code may be delicate to craft.
Next
In my next article I will illustrate:
- How to disable/enable the user interface
- How to indicate activity
- How to show progress
- How to handle worker cancellation
- How to handle worker errors
- How to handle exceptions
Related articles
All articles in this series:
- BackgroundWorkers in Smart Office Scripts – Part 1 – How to set input parameters, and how to receive output values
- BackgroundWorkers in Smart Office Scripts – Part 2 – How to disable/enable the user interface, how to indicate activity, and how to show progress
- BackgroundWorkers in Smart Office Scripts – Part 3 – How to handle worker cancellation
- BackgroundWorkers in Smart Office Scripts – Part 4 – How to handle worker errors and how to handle exceptions
UPDATE: 2012-08-06: Consolidated motive and examples, and added conclusion.
4 thoughts on “BackgroundWorkers in Smart Office Scripts – Part 1”