1# Worker Introduction
2
3With the Worker module, you can provide a multithreaded environment for an application, so that the application can perform a time-consuming operation in a background thread. This greatly prevents a computing-intensive or high-latency task from blocking the running of the main thread. For details about the APIs and their usage, see [Worker](../reference/apis-arkts/js-apis-worker.md).
4
5
6## Worker Operating Mechanism
7
8**Figure 1** Worker operating mechanism
9
10![worker](figures/worker.png)
11
12The thread that creates the worker thread is referred to as the host thread (not necessarily the main thread, since a worker thread can also create another worker thread). A worker thread is also named an actor thread. Each worker thread has an instance independent from the host thread, including the infrastructure, objects, and code segments. Memory overhead exists when each worker thread is started, and therefore the number of worker threads needs to be limited. The worker thread communicates with the host thread by means of message exchange. They use the serialization technique to exchange commands and data.
13
14
15## Precautions for Worker
16
17- A worker thread can be created manually or automatically. In manual creation mode, you must also perform related configurations. For details, see [Precautions for Creating a Worker Thread](#precautions-for-creating-a-worker-thread).
18- The URL of the worker thread file passed in to the constructor function varies according to the version in use. For details, see [Precautions for File URLs](#precautions-for-file-urls).
19- After a worker thread is created, you must manually manage its lifecycle. A maximum of 64 worker threads can run simultaneously. For details, see [Lifecycle Precautions](#lifecycle-precautions).
20- The context objects in different threads are different. Therefore, **Worker** threads can use only thread-safe libraries, rather than UI-related non-thread-safe libraries.
21- A maximum of 16 MB data can be serialized.
22- You must register the **onerror** API in the main thread to listen for worker thread errors, which might cause a JavaScript crash.
23- Worker thread files cannot be used across HAPs.
24- Before referencing the HAR or HSP, configure the dependency on the HAR or HSP. For details, see (https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V5/ide-har-import-V5).
25- [AppStorage](../quick-start/arkts-appstorage.md) cannot be used in the worker thread.
26
27
28### Precautions for Creating a Worker Thread
29
30The worker thread file must be stored in the ***{moduleName}*/src/main/ets/** directory. Otherwise, it will not be packed into the application. A worker thread can be created manually or automatically.
31
32- Manual creation: Manually create the directory and file, and configure the related field in **build-profile.json5** so that the file can be packed into the application.
33
34  Stage model:
35
36  ```json
37  "buildOption": {
38    "sourceOption": {
39      "workers": [
40        "./src/main/ets/workers/worker.ets"
41      ]
42    }
43  }
44  ```
45
46  FA model:
47
48  ```json
49  "buildOption": {
50    "sourceOption": {
51      "workers": [
52        "./src/main/ets/MainAbility/workers/worker.ets"
53      ]
54    }
55  }
56  ```
57
58- Automatic creation: DevEco Studio supports one-click generation of worker threads. Right-click any position in the {moduleName} directory and choose **New > Worker** to generate the template file and configuration information of the worker thread. You do not need to configure the field in **build-profile.json5**.
59
60
61### Precautions for File URLs
62
63  Before calling an API of the Worker module, you must create a **Worker** instance. The constructor function varies in different API versions, and the URL of the worker thread file must be passed in to **scriptURL** of the function.
64
65```ts
66// Import the worker module.
67import { worker } from '@kit.ArkTS';
68
69// Use the following function in API version 9 and later versions:
70const worker1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');
71// Use the following function in API version 8 and earlier versions:
72const worker2: worker.Worker = new worker.Worker('entry/ets/workers/worker.ets');
73```
74
75
76#### File URL Rules in the Stage Model
77
78The requirements for **scriptURL** in the constructor function are as follows:
79
80- **scriptURL** consists of {moduleName}/ets and {relativePath}.
81- {relativePath} is the relative path of the worker thread file to the ***{moduleName}*/src/main/ets/** directory.
82
83(1) Loading a worker thread file of an ability
84
85To load the worker thread file of an ability, use the URL {moduleName}/ets/{relativePath}.
86
87```ts
88import { worker } from '@kit.ArkTS';
89
90// URL of the worker thread file: "entry/src/main/ets/workers/worker.ets"
91const workerStage1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');
92
93// URL of the worker thread file: "phone/src/main/ets/ThreadFile/workers/worker.ets"
94const workerStage2: worker.ThreadWorker = new worker.ThreadWorker('phone/ets/ThreadFile/workers/worker.ets');
95```
96
97(2) Loading a worker thread file in [HSP](../quick-start/in-app-hsp.md)
98
99To load the worker thread file in HSP, use the URL {moduleName}/ets/{relativePath}.
100
101```ts
102import { worker } from '@kit.ArkTS';
103
104// URL of the worker thread file: "hsp/src/main/ets/workers/worker.ets"
105const workerStage3: worker.ThreadWorker = new worker.ThreadWorker('hsp/ets/workers/worker.ets');
106```
107
108(3) Loading a worker thread file in [HAR](../quick-start/har-package.md)
109
110The worker thread file in the HAR may be loaded in either of the following cases:
111
112- @ path loading mode: All types of modules load the worker thread file in the local HAR. The URL is @{moduleName}/ets/{relativePath}.
113
114- Relative path loading mode: The local HAR loads the worker thread file in the package. The URL is the relative path of the file where the Worker object is created to the worker thread file.
115
116>**NOTE**
117>
118>When **useNormalizedOHMUrl** is enabled (the **useNormalizedOHMUrl** field of the **strictMode** attribute in the **build-profile.json5** file at the same level as the entry in the project directory is set to **true**) or the HAR is packed into a third-party package, the worker thread file in the HAR can be loaded using a relative path.
119
120```ts
121import { worker } from '@kit.ArkTS';
122
123// @ Path loading mode
124// URL of the worker thread file: "har/src/main/ets/workers/worker.ets"
125const workerStage4: worker.ThreadWorker = new worker.ThreadWorker('@har/ets/workers/worker.ets');
126
127// Relative path loading mode:
128// URL of the worker thread file: "har/src/main/ets/workers/worker.ets"
129// URL of the file where the Worker object is created: "har/src/main/ets/components/mainpage/MainPage.ets"
130const workerStage5: worker.ThreadWorker = new worker.ThreadWorker('../../workers/worker.ets');
131```
132
133
134#### File URL Rules in the FA Model
135
136  **scriptURL** in the constructor function is the relative path between the worker thread file and "{moduleName}/src/main/ets/MainAbility".
137
138```ts
139import { worker } from '@kit.ArkTS';
140
141// The following three scenarios are involved.
142
143// Scenario 1: URL of the worker thread file: "{moduleName}/src/main/ets/MainAbility/workers/worker.ets"
144const workerFA1: worker.ThreadWorker = new worker.ThreadWorker("workers/worker.ets", {name:"first worker in FA model"});
145
146// Scenario 2: URL of the worker thread file: "{moduleName}/src/main/ets/workers/worker.ets"
147const workerFA2: worker.ThreadWorker = new worker.ThreadWorker("../workers/worker.ets");
148
149// Scenario 3: URL of the worker thread file: "{moduleName}/src/main/ets/MainAbility/ThreadFile/workers/worker.ets"
150const workerFA3: worker.ThreadWorker = new worker.ThreadWorker("ThreadFile/workers/worker.ets");
151```
152
153
154### Lifecycle Precautions
155
156- Creating and terminating worker threads consume performance. Therefore, you are advised to manage available workers and reuse them. The worker threads keep running even when they are idle. When a worker thread is not required, call [terminate()](../reference/apis-arkts/js-apis-worker.md#terminate9) or [close()](../reference/apis-arkts/js-apis-worker.md#close9) to terminate it. If a worker thread is terminated or being terminated, an error is thrown when it is called.
157
158
159- The number of worker threads is determined by the memory management policy. The required memory threshold is the smaller one between 1.5 GB and 60% of the physical memory of the device. If the memory is sufficient, a maximum of 64 worker threads can run simultaneously. If excess worker threads are to be created, the system displays the error message "Worker initialization failure, the number of workers exceeds the maximum." The number of actually running worker threads is dynamically adjusted based on the memory usage. Once the accumulated memory usage of all worker threads and main threads exceeds the threshold, Out of Memory (OOM) error occurs, and applications may crash.
160
161
162## Cross-HAR package loading worker
163
1641. For details about how to create a har, see [Developing a Static Shared Package](../quick-start/har-package.md).
165
1662. Create the Worker thread file in the har file.
167
168   ```ts
169   // worker.ets
170   workerPort.onmessage = (e: MessageEvents) => {
171     console.info("worker thread receive message: ", e.data);
172     workerPort.postMessage('worker thread post message to main thread');
173   }
174   ```
175
1763. Configure the dependency of the HAR package in the oh-package.json5 file of the entry module.
177
178   ```ts
179   // Configure the dependency of the HAR package in the entry module.
180   {
181     "name": "entry",
182     "version": "1.0.0",
183     "description": "Please describe the basic information.",
184     "main": "",
185     "author": "",
186     "license": "",
187     "dependencies": {
188       "har": "file:../har"
189     }
190   }
191   ```
192
1934. Load the worker thread file in the HAR package in the entry module.
194
195   ```ts
196   // Index.ets
197   import { worker } from '@kit.ArkTS';
198
199   @Entry
200   @Component
201   struct Index {
202     @State message: string = 'Hello World';
203
204     build() {
205       RelativeContainer() {
206         Text(this.message)
207           .id('HelloWorld')
208           .fontSize(50)
209           .fontWeight(FontWeight.Bold)
210           .alignRules({
211             center: { anchor: '__container__', align: VerticalAlign.Center },
212             middle: { anchor: '__container__', align: HorizontalAlign.Center }
213           })
214           .onClick(() => {
215             // Use @ to identify the path loading mode and load the Worker thread file in the har.
216             let workerInstance = new worker.ThreadWorker('@har/ets/workers/worker.ets');
217             workerInstance.onmessage = () => {
218               console.info('main thread onmessage');
219             };
220             workerInstance.postMessage('hello world');
221           })
222       }
223       .height('100%')
224       .width('100%')
225     }
226   }
227   ```
228
229
230## Multi-level worker lifecycle management
231Multi-level Workers can be created (that is, a hierarchical thread relationship is formed by creating child Workers through the parent Worker), and the lifecycle of Worker threads is managed by users. Therefore, the lifecycle of multi-level Workers must be correctly managed. If the child Worker is not stopped when the parent Worker is destroyed, unexpected results may occur. You are advised to ensure that the lifecycle of the child Worker is always within the lifecycle of the parent Worker and destroy all child Workers before destroying the parent Worker.
232
233
234### Recommended Example
235
236```ts
237// Create a worker thread (parent worker) in the main thread and create a worker thread (child worker) in the worker thread.
238// main thread
239import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
240
241// Create a Worker instance in the main thread.
242const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
243
244parentworker.onmessage = (e: MessageEvents) => {
245  console.info ("The main thread receives the parent worker thread information" + e.data);
246}
247
248parentworker.onexit = () => {
249  console.info ("The parent worker exits.");
250}
251
252parentworker.onerror = (err: ErrorEvent) => {
253  console.info ("The main thread receives an error from the parent worker." + err);
254}
255
256parentworker.postMessage ("The main thread sends a message to the parentworker. Recommended example");
257```
258
259```ts
260// parentworker.ets
261import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
262
263// Create an object in the worker thread for communicating with the main thread.
264const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
265
266workerPort.onmessage = (e : MessageEvents) => {
267  if (e.data = = "The main thread sends a message to the parent worker.-Recommended example") {
268    let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
269
270    childworker.onmessage = (e: MessageEvents) => {
271      console.info ("The parent Worker receives information from the child Worker" + e.data);
272      if (e.data = = "The child worker sends information to the parent worker.") {
273        workerPort.postMessage ("The parent worker sends information to the main thread.");
274      }
275    }
276
277    childworker.onexit = () => {
278      console.info ("The child worker exits.");
279      // Destroy the parent Worker after the child Worker exits.
280      workerPort.close();
281    }
282
283    childworker.onerror = (err: ErrorEvent) => {
284      console.info ("An error occurred on the child Worker." + err);
285    }
286
287    childworker.postMessage ("The parent Worker sends information to the child Worker. Recommended example");
288  }
289}
290```
291
292```ts
293// childworker.ets
294import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
295
296// Create an object in the worker thread for communicating with the main thread.
297const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
298
299workerPort.onmessage = (e: MessageEvents) => {
300  if (e.data = = "The parent Worker sends information to the child Worker. Recommended example.") {
301    // Service logic of the sub-worker thread...
302    console.info ("The service execution is complete, and the child Worker is destroyed.");
303    workerPort.close();
304  }
305}
306```
307
308
309### Examples Not Recommended
310
311It is not recommended that a child Worker send messages to the parent Worker after the parent Worker is destroyed.
312
313```ts
314// main thread
315import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
316
317const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
318
319parentworker.onmessage = (e: MessageEvents) => {
320  console.info("The main thread receives the parent worker information" + e.data);
321}
322
323parentworker.onexit = () => {
324  console.info("The parent worker exits.");
325}
326
327parentworker.onerror = (err: ErrorEvent) => {
328  console.info("The main thread receives an error from the parent worker." + err);
329}
330
331parentworker.postMessage ("The main thread sends a message to the parent worker.");
332```
333
334```ts
335// parentworker.ets
336import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
337
338const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
339
340workerPort.onmessage = (e : MessageEvents) => {
341  console.info("The parent worker receives information from the main thread." + e.data);
342
343  let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets")
344
345  childworker.onmessage = (e: MessageEvents) => {
346    console.info("The parent Worker receives information from the child Worker" + e.data);
347  }
348
349  childworker.onexit = () => {
350    console.info("The child worker exits.");
351    workerPort.postMessage("The parent worker sends information to the main thread.");
352  }
353
354  childworker.onerror = (err: ErrorEvent) => {
355    console.info("An error occurred on the child Worker." + err);
356  }
357
358  childworker.postMessage("The parent worker sends information to the child worker.");
359
360  // Destroy the parent Worker after the child Worker is created.
361  workerPort.close();
362}
363```
364
365```ts
366// childworker.ets
367import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
368
369const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
370
371workerPort.onmessage = (e: MessageEvents) => {
372  console.info("The child Worker receives the message" + e.data);
373
374  // After the parent Worker is destroyed, the child Worker sends information to the parent Worker. The behavior is unpredictable.
375  workerPort.postMessage("The child Worker sends information to the parent Worker.");
376  setTimeout(() => {
377    workerPort.postMessage("The child Worker sends information to the parent Worker.");
378  }, 1000);
379}
380```
381
382You are not advised to create a child Worker in the parent Worker thread before and after the parent Worker initiates the synchronous call of the destruction operation. You are not advised to create a child Worker in the parent Worker thread if you are not sure whether the parent Worker initiates a destruction operation. That is, ensure that the parent Worker thread is always alive before the child Worker thread is successfully created.
383
384```ts
385// main thread
386import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
387
388const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
389
390parentworker.onmessage = (e: MessageEvents) => {
391  console.info("The main thread receives the parent worker information" + e.data);
392}
393
394parentworker.onexit = () => {
395  console.info("The parent worker exits.");
396}
397
398parentworker.onerror = (err: ErrorEvent) => {
399  console.info("The main thread receives an error from the parent worker." + err);
400}
401
402parentworker.postMessage("The main thread sends a message to the parent worker.");
403```
404
405```ts
406// parentworker.ets
407import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
408
409const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
410
411workerPort.onmessage = (e : MessageEvents) => {
412  console.info("The parent worker receives information from the main thread." + e.data);
413
414  // Create a child Worker after the parent Worker is destroyed. The behavior is unpredictable.
415  workerPort.close();
416  let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
417
418  // Destroy the parent Worker before the child Worker thread confirms that the creation is successful. The behavior is unpredictable.
419  // let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
420  // workerPort.close();
421
422  childworker.onmessage = (e: MessageEvents) => {
423    console.info("The parent Worker receives information from the child Worker" + e.data);
424  }
425
426  childworker.onexit = () => {
427    console.info("The child worker exits.");
428    workerPort.postMessage("The parent worker sends information to the main thread.");
429  }
430
431  childworker.onerror = (err: ErrorEvent) => {
432    console.info("An error occurred on the child Worker." + err);
433  }
434
435  childworker.postMessage("The parent worker sends information to the child worker.");
436}
437```
438
439```ts
440// childworker.ets
441import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
442
443const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
444
445workerPort.onmessage = (e: MessageEvents) => {
446  console.info("The child Worker receives the message" + e.data);
447}
448```
449