Skip to content

Commit 9a82f17

Browse files
committed
added async page
1 parent 1a73068 commit 9a82f17

2 files changed

Lines changed: 195 additions & 1 deletion

File tree

docs/Develop/async.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
id: async
3+
title: Asynchronous Execution
4+
---
5+
6+
4D supports both **synchronous** and **asynchronous** execution modes, allowing developers to choose the best approach based on performance, responsiveness, and workload distribution.
7+
8+
## Basics
9+
10+
#### Synchronous Execution
11+
12+
Synchronous execution follows a **sequential** flow, a step-by-step where each instruction must complete before the next one starts. This means the execution thread is blocked until the operation finishes.
13+
14+
Synchronous execution is used when:
15+
- Task execution must follow a strict order.
16+
- Performance impact is minimal (e.g., quick operations).
17+
- Running in a single-threaded context where blocking is acceptable.
18+
- Synchronous execution blocks the UI and is best suited for quick, ordered tasks where blocking is acceptable.
19+
20+
#### Asynchronous Execution
21+
22+
Asynchronous execution is **event-driven** and allows tasks other operations to complete. It relies on **callbacks**, **workers**, and **event handlers** to manage execution flow.
23+
24+
Asynchronous execution is used when:
25+
- An operation takes a long time (e.g., waiting for a server response).
26+
- Responsiveness is critical (e.g., UI interactions).
27+
- Performing background tasks, network communication, or parallel processing.
28+
29+
Choosing Between Synchronous and Asynchronous Execution:
30+
31+
| Scenario | Best Approach |
32+
|----------|---------------|
33+
| Quick operations with minimal processing | **Synchronous** |
34+
| Tasks requiring strict execution order | **Synchronous** |
35+
| Long-running background tasks | **Asynchronous** |
36+
| Long-running UI interactions | **Asynchronous** |
37+
| Short-running UI interactions | **Synchronous** |
38+
| High-performance, multi-threaded workloads | **Asynchronous** |
39+
40+
## Core principles
41+
42+
4D provides built-in **asynchronous execution** capabilities through various classes and commands. These allow background task execution, network communication, and large data processing, while waiting other operations to complete without blocking the current process.
43+
44+
The general concept of asynchronous event management in 4D is based on an asynchronous messaging model using **workers** (processes that listen to events) and **callbacks** (functions or formulas automatically invoked when an event occurs). Instead of waiting for a result (synchronous mode), you provide a function that will be automatically called when the desired event occurs. Callbacks can be passed as class functions (recommended) or Formula objects.
45+
46+
This model is common to [`CALL WORKER`](../commands-legacy/call-worker.md), [`CALL FORM`](../commands-legacy/call-form.md), and [classes that support aynchronous execution](#asynchronous-programming-with-4d-classes). All these commands/classes start an operation that runs in the background. The statement that launches the operation returns immediately, without waiting for the operation to finish.
47+
48+
### Workers
49+
50+
Asynchronous programming relies on a system of [**workers**](../Develop/processes.md#worker-processes) (worker processes), which allows code to be executed in parallel without blocking the main process. This is particularly useful for long tasks (such as HTTP calls, executing external processes, background processing), while keeping the user interface responsive.
51+
52+
Using worker processes in asynchronous programming **is mandatory** since "classic" processes automatically terminate their execution when the process method ends, thus using callbacks is not possible. A worker process stays alive and can **listen to events**.
53+
54+
### Event queue (mailbox)
55+
56+
Each worker (or form window for [`CALL FORM`](../commands-legacy/call-form.md)) has its own message queue. [`CALL WORKER`](../commands-legacy/call-worker.md) or [`CALL FORM`](../commands-legacy/call-form.md) simply posts a message to this queue. The worker handles messages one by one, in the order they arrive, within its own context. Process variables, current selections, etc. are preserved.
57+
58+
### Bidirectional communication via messages
59+
60+
The calling process posts a message then the worker executes it. The worker can in turn post a message (via [`CALL WORKER`](../commands-legacy/call-worker.md) or [`CALL FORM`](../commands-legacy/call-form.md)) back to the caller or another worker to notify an event (task completion, data received, error, progress, etc.). This mechanism replaces the classic return of synchronous calls.
61+
62+
### Event listening
63+
64+
In event-driven development, it is obvious that some code must be able to listen for incoming events. Events can be generated by the user interface (such as a mouse click on an object or a keyboard key pressed) or by any other interaction such as an http request or the end of another action. For example, when a form is displayed using the `DIALOG` command, user actions can trigger events that your code can process. A click on a button will trigger the code associated to the button.
65+
66+
In the context of asynchronous execution, the following features place your code in listening mode:
67+
68+
- [`CALL WORKER`](../commands-legacy/call-worker.md) executes the code for which it has been called, then returns to a listening status from where it can be called afterwards.
69+
- [`CALL FORM`](../commands-legacy/call-form.md) opens a form and makes it listen for incoming messages from the event queue.
70+
- a call for a `wait()` listens for `terminate()` or `shutdown()` in a callback from any other instance.
71+
72+
### Event triggering
73+
74+
Events are automatically triggered during the execution flow and passed to your corresponding callbacks. You can force the triggering of events by calling `terminate()` or `shutdown()` during a `wait()`.
75+
76+
77+
### Callback execution context
78+
79+
When 4D execute one of your callbacks, it does so in the context of the current process (worker), i.e. if your object is instantiated inside a form, the callback function will be executed in the context of that same form.
80+
81+
For callbacks to work properly in fully asynchronous mode, the operation should generally be launched from a worker (via `CALL WORKER`). If launched from a process handling UI, some callbacks may not be called until the UI is listening events.
82+
83+
84+
85+
### Releasing an asynchronous object
86+
87+
In 4D, all objects are released [when no more references](../Concepts/dt_object.md#resources) to them exist in memory. This typically occurs at the end of a method execution for local variables.
88+
89+
For asynchronous classes, an **extra reference** is always maintained by 4D in the process that instantiated the object. This reference is only released when the operation is finished, i.e. after the `onTerminate` event is triggered. This automatic referencing allows your object to survive even if you don't have referenced it specifically in a variable.
90+
91+
If you want to "force" the release of an object at any moment, use a `.shutdown()` or `terminate()` function; it triggers the onTerminate` event ànd thus releases the object.
92+
93+
### Examples illustrating the common concept
94+
95+
|Feature|Async Launch|Callback / Event Handling|
96+
|---|----|---|
97+
|CALL WORKER|CALL WORKER("wk"; "MyMethod"; $params)|MyMethod is called with $params|
98+
|CALL FORM|CALL FORM($win; "MyMethod"; $params)|MyMethod is called with $params|
99+
|4D.SystemWorker|4D.SystemWorker.new(cmd; $options)|Callbacks: onData, onResponse, onError, onTerminate|
100+
101+
## Asynchronous programming with 4D classes
102+
103+
Several 4D classes support asynchronous processing:
104+
105+
- [`HTTPRequest`](../API/HTTPRequestClass.md) – Handles asynchronous HTTP requests and responses.
106+
- [`SystemWorker`](../API/SystemWorkerClass.md) – Executes external processes asynchronously.
107+
- [`TCPConnection`](../API/TCPConnectionClass.md) – Manages TCP client connections with event-driven callbacks.
108+
- [`TCPListener`](../API/TCPListenerClass.md) – Manages TCP server connections.
109+
- [`UDPSocket`](../API/UDPSocketClass.md) – Sends and receives UDP packets.
110+
- [`WebSocket`](../API/WebSocketClass.md) – Manages WebSocket client connections.
111+
- [`WebSocketServer`](../API/WebSocketServerClass.md) – Manages WebSocket server connections.
112+
113+
All these classes follow the same rules regarding asynchronous execution. Their constructor accepts an *options* parameter that is used to configure your asynchronous object. It is recommended that the *options* object is a [user class](../Concepts/classes.md) instance which has callback functions. For example, you can create an `onResponse()` function in the class, it will be automatically called asychronously when a *reponse* event is fired.
114+
115+
We recommend the following sequence:
116+
117+
1. You create the user class where you declare callback functions, for example a `cs.Params` with `onError()` and `onResponse()` functions.
118+
2. You instantiate the user class (in our example using `cs.Params.new()`) that will configure your asynchronous object.
119+
3. You call the constructor of the 4D class (for example `4D.SystemWorker.new()`) and pass the *options* object as parameter. It starts the operations passed immediately without delay.
120+
121+
Here is a full example of implementation of an *options* object based upon a user class:
122+
123+
```4d
124+
// asynchronous code creation
125+
var $options:=cs.Params.new(10) //see cs.Params class code below
126+
var $systemworker:=4D.SystemWorker.new("/bin/ls -l /Users ";$options)
127+
128+
129+
// "Params" class
130+
131+
Class constructor ($timeout : Real)
132+
This.dataType:="text"
133+
This.data:=""
134+
This.dataError:=""
135+
This.timeout:=$timeout
136+
137+
Function onResponse($systemWorker : Object)
138+
This._createFile("onResponse"; $systemWorker.response)
139+
140+
Function onData($systemWorker : Object; $info : Object)
141+
This.data+=$info.data
142+
This._createFile("onData";this.data)
143+
144+
Function onDataError($systemWorker : Object; $info : Object)
145+
This.dataError+=$info.data
146+
This._createFile("onDataError";this.dataError)
147+
148+
Function onTerminate($systemWorker : Object)
149+
var $textBody : Text
150+
$textBody:="Response: "+$systemWorker.response
151+
$textBody+="ResponseError: "+$systemWorker.responseError
152+
This._createFile("onTerminate"; $textBody)
153+
154+
Function _createFile($title : Text; $textBody : Text)
155+
TEXT TO DOCUMENT(Get 4D folder(Current resources folder)+$title+".txt"; $textBody)
156+
157+
```
158+
159+
Note that `onResponse`, `onData`, `onDataError`, and `onTerminate` are functions supported by [`4D.SystemWorker`](../API/SystemWorkerClass.md).
160+
161+
Once the user class is instantiated; 4D is put in [event listening](#event-listening) mode, in which case 4D can [trigger an event](#event-triggering) that calls the corresponding function in the user class.
162+
163+
:::tip
164+
165+
In some cases, you might want to use formulas as property values instead of class functions. Although it is not the best practice, a syntax such as the following is supported:
166+
```4d
167+
var $options.onResponse:=Formula(myMethod)
168+
```
169+
:::
170+
171+
172+
173+
174+
## Synchronous execution in asynchronous code
175+
176+
Even when using modern, asynchronous code, you may need to introduce a degree of synchronous execution. For example, you may want a function to wait for a certain amount of time to get a result. It could the case with guaranteed fast network connections or system workers. Then, you can enforce synchronous execution using the `wait()` function.
177+
178+
The **`.wait()`** function pauses execution of the current process and puts 4D in [event listening](#event-listening) mode. Keep in mind that it will trigger events received from any sources, not only from the object on which the `wait()` function was called.
179+
180+
The `wait()` function returns when the `onTerminate` event has been triggered on the object, or when the provided timeout (if any) has expired. Consequently, you can explicitly exit from a `.wait()` by calling `shutdown()` or `terminate()` from within a callback. Otherwise, the `.wait()` is exited when the current operation ends.
181+
182+
Example:
183+
184+
```4d
185+
var $options:=cs.Params.new()
186+
var $systemworker:=4D.SystemWorker.new("/bin/ls -l /Users ";$options)
187+
$systemworker.wait(0.5) // Waits for up to 0.5 seconds for get file info
188+
```
189+
190+
## See also
191+
192+
[Blog post: Launch an external process asynchronously](https://blog.4d.com/launch-an-external-process-asynchronously/)<br/>
193+
[Asynchronous Call](../aikit/asynchronous-call.md)

sidebars.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2304,7 +2304,8 @@ module.exports =
23042304
},
23052305
items: [
23062306
"Develop/processes",
2307-
"Develop/preemptive-processes"
2307+
"Develop/preemptive-processes",
2308+
"Develop/async"
23082309
]
23092310
},
23102311
{

0 commit comments

Comments
 (0)