• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Worker
2
3Worker primarily provides a multithreaded runtime environment for applications, allowing them to separate from the host thread during execution. This enables scripts to run in background threads for time-consuming operations, significantly reducing the likelihood of blocking the host thread during compute-intensive or high-latency tasks. 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
12When creating a Worker, the thread that initiates it is referred to as the host thread (not necessarily the main thread, as worker threads can also create child Workers). The Worker itself runs in its own thread, known as a Worker thread or actor thread. Each Worker thread operates independently, with its own infrastructure, objects, and code segments, which incurs some memory overhead for each Worker. Therefore, the number of Worker threads should 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 can be created manually or automatically. In manual creation mode, you must also synchronize the related configuration. For details, see [Precautions for Creating a Worker](#precautions-for-creating-a-worker).
18- When using Worker capabilities, the URL of the Worker thread file passed in the constructor varies by API version. For specifics, see [Precautions for File URLs](#precautions-for-file-urls).
19- After a Worker is created, its lifecycle must be managed manually. A maximum of 64 Worker threads can run simultaneously, and the total number cannot exceed 80, including those created with [napi_create_ark_runtime](../reference/native-lib/napi.md#napi_create_ark_runtime). For details, see [Precautions for Lifecycle Management](#precautions-for-lifecycle-management).
20- The context objects in different threads are different. Therefore, Worker threads can use only thread-safe libraries. For example, non-thread-safe UI-related libraries cannot be used.
21- A maximum of 16 MB data can be serialized.
22- When using the Worker module, you are advised to register the **onAllErrors** callback in the host thread in API version 18 or later to capture various exceptions that may occur during the lifecycle of the Worker thread. In API version 15 or earlier, register the **onerror** callback. If neither of them is registered, JS crash occurs when the Worker thread is abnormal. Note that the **onerror** callback can only capture synchronous exceptions within the **onmessage** callback. Once an exception is captured, the Worker thread will proceed to the destruction process and cannot be used. For details, see [Behavior Differences Between onAllErrors and onerror](#behavior-differences-between-onallerrors-and-onerror).
23- Worker thread files cannot be used across HAPs.
24- Before referencing a HAR or HSP, configure the dependency on the HAR or HSP. For details, see [Referencing a Shared Package](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 Worker threads.
26- Starting from API version 18, the priority of the Worker thread can be specified in the [WorkerOptions](../reference/apis-arkts/js-apis-worker.md#workeroptions) parameter of the constructor.
27
28### Precautions for Creating a Worker
29
30The Worker thread file must be placed in the ***{moduleName}*/src/main/ets/** directory to be included in the application package. There are two ways to create Worker thread directories and files: manually and automatically.
31
32- Manual creation: Manually create the directory and file, and configure the related field in **build-profile.json5** so that the Worker thread file can be packed into the application package.
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 Workers. Right-click any position in the {moduleName} directory and choose **New > Worker** to generate the Worker template file and configuration information. There is no need to configure the fields in **build-profile.json5**.
59
60
61### Precautions for File URLs
62
63Before calling an API of the Worker module, you must create a Worker object. The constructor is related to the API version and requires the URL to the Worker thread file to be passed in **scriptURL**.
64
65```ts
66// Import the 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 Stage Model
77
78The requirements for **scriptURL** in the constructor 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: "testworkers/src/main/ets/ThreadFile/workers/worker.ets"
94const workerStage2: worker.ThreadWorker = new worker.ThreadWorker('testworkers/ets/ThreadFile/workers/worker.ets');
95```
96
97(2) Loading a Worker thread file from an [HSP](../quick-start/in-app-hsp.md)
98
99To load the Worker thread file from an 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 from an [HAR](../quick-start/har-package.md)
109
110There are two scenarios for loading a Worker thread file from an HAR:
111
112- @ path loading: All types of modules load the Worker thread file from the local HAR. The URL is @{moduleName}/ets/{relativePath}.
113
114- Relative path loading: The local HAR loads the Worker thread file within the same 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** property in the application-level **build-profile.json5** file at the same level as the entry in the project directory is set to **true**) or when the HAR is used as a third-party package, the Worker thread file contained the HAR can be loaded using a relative path.
119
120```ts
121import { worker } from '@kit.ArkTS';
122
123// @ path loading:
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:
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 FA Model
135
136**scriptURL** in the constructor is the relative path from the Worker thread file to "{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### Precautions for Lifecycle Management
155
156- Creating and destroying Worker consume system resources. Therefore, you are advised to manage created Workers efficiently and reuse them when possible. Idle Workers continue to run. When a Worker is no longer needed, call [terminate()](../reference/apis-arkts/js-apis-worker.md#terminate9) or [close()](../reference/apis-arkts/js-apis-worker.md#close9) to destroy it actively. If a Worker is in a non-running state such as destroyed or being destroyed, calling its functional interfaces will throw corresponding errors.
157
158
159- The number of Workers is determined by the memory management policy, with a set memory threshold being the smaller of 1.5 GB and 60% of the device's physical memory. Under memory constraints, a maximum of 64 Workers can run simultaneously. If an attempt is made to create more Workers than this limit, the system displays the error message "Worker initialization failure, the number of Workers exceeds the maximum." The actual number of running Workers will be dynamically adjusted based on current memory usage. Once the cumulative memory usage of all Workers and the main thread exceeds the set threshold, Out of Memory (OOM) error occurs, and applications may crash.
160
161
162### Behavior Differences Between onAllErrors and onerror
163
1641. Exception Capture Range
165
166    **onAllErrors** can capture global exceptions generated during the **onmessage** callback, timer callback, and file execution of the Worker thread.
167
168    **onerror** can capture only exceptions generated by synchronous methods within the **onmessage** callback. It cannot capture exceptions from multithreaded callbacks or modularization-related exceptions.
169
1702. Thread State After Exception Capture
171
172    After an exception is captured by **onAllErrors**, the Worker thread remains alive and can continue to be used. This allows you to perform additional operations after an exception is captured, without worrying about the thread being terminated.
173
174    Once an exception is captured by **onerror**, the Worker thread enters the destruction process and cannot be used. This means that after **onerror** is triggered, the Worker thread will be terminated, and subsequent operations cannot proceed.
175
1763. Applicable Scenarios
177
178    **onAllErrors** applies to scenarios where all types of exceptions in the Worker thread need to be captured, especially in complex scenarios where the Worker thread must continue running after an exception occurs.
179
180    **onerror** is appropriate for simple scenarios where only synchronous exceptions in the **onmessage** callback need to be captured. Since the thread is destroyed after an exception is captured, it is best used when the Worker thread is not needed afterward.
181
182    **onAllErrors** is recommended because it provides more comprehensive exception capture capabilities and does not lead to thread termination.
183
184
185## Basic Usage Example of Worker
186
1871. In DevEco Studio, right-click any position in the {moduleName} directory and choose **New > Worker** to automatically generate the Worker template file and configuration information. This section uses the creation of "worker" as an example.
188
189   You can also manually create Worker thread files. For specific methods and related considerations, see [Precautions for Creating a Worker](#precautions-for-creating-a-worker).
190
1912. Import the Worker module.
192
193    ```ts
194    // Index.ets
195    import { ErrorEvent, MessageEvents, worker } from '@kit.ArkTS'
196    ```
197
1983. In the host thread, call [constructor()](../reference/apis-arkts/js-apis-worker.md#constructor9) of **ThreadWorker** to create a Worker object, and register callback functions. The calling thread is the host thread.
199
200      ```ts
201      // Index.ets
202      @Entry
203      @Component
204      struct Index {
205        @State message: string = 'Hello World';
206
207        build() {
208          RelativeContainer() {
209            Text(this.message)
210              .id('HelloWorld')
211              .fontSize(50)
212              .fontWeight(FontWeight.Bold)
213              .alignRules({
214                center: { anchor: '__container__', align: VerticalAlign.Center },
215                middle: { anchor: '__container__', align: HorizontalAlign.Center }
216              })
217              .onClick(() => {
218                // Create a Worker object.
219                let workerInstance = new worker.ThreadWorker('entry/ets/workers/worker.ets');
220
221                // Register the onmessage callback. When the host thread receives a message from the Worker thread through the workerPort.postMessage interface, this callback is invoked and executed in the host thread.
222                workerInstance.onmessage = (e: MessageEvents) => {
223                  let data: string = e.data;
224                  console.info("workerInstance onmessage is: ", data);
225                }
226
227                // Register the onAllErrors callback to capture global exceptions generated during the onmessage callback, timer callback, and file execution of the Worker thread. This callback is executed in the host thread.
228                workerInstance.onAllErrors = (err: ErrorEvent) => {
229                  console.info("workerInstance onAllErrors message is: " + err.message);
230                }
231
232                // Register the onmessageerror callback. When the Worker object receives a message that cannot be serialized, this callback is invoked and executed in the host thread.
233                workerInstance.onmessageerror = () => {
234                  console.info('workerInstance onmessageerror');
235                }
236
237                // Register the onexit callback. When the Worker object is destroyed, this callback is invoked and executed in the host thread.
238                workerInstance.onexit = (e: number) => {
239                  // When the Worker object exits normally, the code is 0. When the Worker object exits abnormally, the code is 1.
240                  console.info("workerInstance onexit code is: ", e);
241                }
242
243                // Send a message to the Worker thread.
244                workerInstance.postMessage('1');
245              })
246          }
247          .height('100%')
248          .width('100%')
249        }
250      }
251      ```
252
2534. Register the callback functions in the Worker thread file.
254
255      ```ts
256      // worker.ets
257      import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
258
259      const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
260
261      // Register the onmessage callback. When the Worker thread receives a message from the host thread through the postMessage interface, this callback is invoked and executed in the Worker thread.
262      workerPort.onmessage = (e: MessageEvents) => {
263        let data: string = e.data;
264        console.info('workerPort onmessage is: ', data);
265
266        // Send a message to the main thread.
267        workerPort.postMessage('2');
268      }
269
270      // Register the onmessageerror callback. When the Worker object receives a message that cannot be serialized, this callback is invoked and executed in the Worker thread.
271      workerPort.onmessageerror = () => {
272        console.info('workerPort onmessageerror');
273      }
274
275      // Register the onerror callback. When an exception occurs during the execution of the Worker thread, this callback is invoked and executed in the Worker thread.
276      workerPort.onerror = (err: ErrorEvent) => {
277        console.info('workerPort onerror err is: ', err.message);
278      }
279      ```
280
281
282## Loading Worker Across HARs
283
2841. Create an HAR. For details, see [HAR](../quick-start/har-package.md).
285
2862. Create the Worker thread file in the HAR.
287
288   ```ts
289   // worker.ets
290   workerPort.onmessage = (e: MessageEvents) => {
291     console.info("worker thread receive message: ", e.data);
292     workerPort.postMessage('worker thread post message to main thread');
293   }
294   ```
295
2963. Configure the dependency of the HAR in the **oh-package.json5** file of the entry module.
297
298   ```ts
299   // Configure the dependency of the HAR in the entry module.
300   {
301     "name": "entry",
302     "version": "1.0.0",
303     "description": "Please describe the basic information.",
304     "main": "",
305     "author": "",
306     "license": "",
307     "dependencies": {
308       "har": "file:../har"
309     }
310   }
311   ```
312
3134. Load the Worker thread file from the HAR in the entry module.
314
315   ```ts
316   // Index.ets
317   import { worker } from '@kit.ArkTS';
318
319   @Entry
320   @Component
321   struct Index {
322     @State message: string = 'Hello World';
323
324     build() {
325       RelativeContainer() {
326         Text(this.message)
327           .id('HelloWorld')
328           .fontSize(50)
329           .fontWeight(FontWeight.Bold)
330           .alignRules({
331             center: { anchor: '__container__', align: VerticalAlign.Center },
332             middle: { anchor: '__container__', align: HorizontalAlign.Center }
333           })
334           .onClick(() => {
335             // Use @ path loading mode and load the Worker thread file from the HAR.
336             let workerInstance = new worker.ThreadWorker('@har/ets/workers/worker.ets');
337             workerInstance.onmessage = () => {
338               console.info('main thread onmessage');
339             };
340             workerInstance.postMessage('hello world');
341           })
342       }
343       .height('100%')
344       .width('100%')
345     }
346   }
347   ```
348
349
350## Multi-Level Worker Lifecycle Management
351Multi-level Workers can be created (a hierarchical thread relationship is formed by the mechanism of creating child Workers through parent Workers), and the lifecycle of Worker threads should be manually managed. Therefore, it is important to properly manage the lifecycle of multi-level Workers. If a parent Worker is destroyed without terminating its child Workers, unpredictable results may occur. It is recommended that you ensure the lifecycle of child Workers always remains within the lifecycle of the parent Worker and that all child Workers are terminated before destroying the parent Worker.
352
353
354### Recommended Usage Example
355
356```ts
357// Create a Worker thread (parent Worker) in the main thread, and create a Worker thread (child Worker) in the parent Worker.
358// main thread
359import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
360
361// Create a parent Worker object in the main thread.
362const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
363
364parentworker.onmessage = (e: MessageEvents) => {
365  console.info("The main thread receives a message from the parent Worker" + e.data);
366}
367
368parentworker.onexit = () => {
369  console.info("The parent Worker exits");
370}
371
372parentworker.onAllErrors = (err: ErrorEvent) => {
373  console.info("The main thread receives an error from the parent Worker" + err);
374}
375
376parentworker.postMessage("The main thread sends a message to the parent Worker - recommended example");
377```
378
379```ts
380// parentworker.ets
381import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
382
383// Create an object in the parent Worker for communicating with the main thread.
384const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
385
386workerPort.onmessage = (e : MessageEvents) => {
387  if (e.data == "The main thread sends a message to the parent Worker - recommended example") {
388    let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
389
390    childworker.onmessage = (e: MessageEvents) => {
391      console.info("The parent Worker receives a message from the child Worker" + e.data);
392      if (e.data == "The child Worker sends information to the parent Worker") {
393        workerPort.postMessage("The parent Worker sends a message to the main thread");
394      }
395    }
396
397    childworker.onexit = () => {
398      console.info("The child Worker exits");
399      // Destroy the parent Worker after the child Worker exits.
400      workerPort.close();
401    }
402
403    childworker.onAllErrors = (err: ErrorEvent) => {
404      console.info("An error occurred on the child Worker" + err);
405    }
406
407    childworker.postMessage("The parent Worker sends a message to the child Worker - recommended example");
408  }
409}
410```
411
412```ts
413// childworker.ets
414import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
415
416// Create an object in the child Worker for communicating with the parent Worker.
417const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
418
419workerPort.onmessage = (e: MessageEvents) => {
420  if (e.data == "The parent Worker sends a message to the child Worker - recommended example") {
421    // Service logic of the child Worker...
422    console.info("The service execution is complete, and the child Worker is destroyed");
423    workerPort.close();
424  }
425}
426```
427
428
429### Not Recommended Example
430
431It is not recommended that a child Worker send messages to the parent Worker after the parent Worker is destroyed.
432
433```ts
434// main thread
435import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
436
437const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
438
439parentworker.onmessage = (e: MessageEvents) => {
440  console.info("The main thread receives a message from the parent Worker" + e.data);
441}
442
443parentworker.onexit = () => {
444  console.info("The parent Worker exits");
445}
446
447parentworker.onAllErrors = (err: ErrorEvent) => {
448  console.info("The main thread receives an error from the parent Worker" + err);
449}
450
451parentworker.postMessage("The main thread sends a message to the parent Worker");
452```
453
454```ts
455// parentworker.ets
456import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
457
458const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
459
460workerPort.onmessage = (e : MessageEvents) => {
461  console.info("The parent Worker receives a message from the main thread" + e.data);
462
463  let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets")
464
465  childworker.onmessage = (e: MessageEvents) => {
466    console.info("The parent Worker receives a message from the child Worker" + e.data);
467  }
468
469  childworker.onexit = () => {
470    console.info("The child Worker exits");
471    workerPort.postMessage("The parent Worker sends a message to the main thread");
472  }
473
474  childworker.onAllErrors = (err: ErrorEvent) => {
475    console.info("An error occurred on the child Worker" + err);
476  }
477
478  childworker.postMessage("The parent Worker sends a message to the child Worker");
479
480  // Destroy the parent Worker after the child Worker is created.
481  workerPort.close();
482}
483```
484
485```ts
486// childworker.ets
487import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
488
489const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
490
491workerPort.onmessage = (e: MessageEvents) => {
492  console.info("The child Worker receives a message" + e.data);
493
494  // After the parent Worker is destroyed, the child Worker sends a message to the parent Worker. The behavior is unpredictable.
495  workerPort.postMessage("The child Worker sends a message to the parent Worker");
496  setTimeout(() => {
497    workerPort.postMessage("The child Worker sends a message to the parent Worker");
498  }, 1000);
499}
500```
501
502You are not advised to create a child Worker in the parent Worker before and after a synchronous call that clearly triggers the destruction of the parent Worker. Furthermore, avoid creating a child Worker in the parent Worker if there is any uncertainty about whether the parent Worker is being destroyed. Ensure that the parent Worker remains active before the child Worker is successfully created.
503
504```ts
505// main thread
506import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
507
508const parentworker = new worker.ThreadWorker("entry/ets/workers/parentworker.ets");
509
510parentworker.onmessage = (e: MessageEvents) => {
511  console.info("The main thread receives a message from the parent Worker" + e.data);
512}
513
514parentworker.onexit = () => {
515  console.info("The parent Worker exits");
516}
517
518parentworker.onAllErrors = (err: ErrorEvent) => {
519  console.info("The main thread receives an error from the parent Worker" + err);
520}
521
522parentworker.postMessage("The main thread sends a message to the parent Worker");
523```
524
525```ts
526// parentworker.ets
527import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
528
529const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
530
531workerPort.onmessage = (e : MessageEvents) => {
532  console.info("The parent Worker receives a message from the main thread" + e.data);
533
534  // Create a child Worker after the parent Worker is destroyed. The behavior is unpredictable.
535  workerPort.close();
536  let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
537
538  // Destroy the parent Worker before it is confirmed that the child Worker is successfully created. The behavior is unpredictable.
539  // let childworker = new worker.ThreadWorker("entry/ets/workers/childworker.ets");
540  // workerPort.close();
541
542  childworker.onmessage = (e: MessageEvents) => {
543    console.info("The parent Worker receives a message from the child Worker" + e.data);
544  }
545
546  childworker.onexit = () => {
547    console.info("The child Worker exits");
548    workerPort.postMessage("The parent Worker sends a message to the main thread");
549  }
550
551  childworker.onAllErrors = (err: ErrorEvent) => {
552    console.info("An error occurred on the child Worker" + err);
553  }
554
555  childworker.postMessage("The parent Worker sends a message to the child Worker");
556}
557```
558
559```ts
560// childworker.ets
561import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
562
563const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
564
565workerPort.onmessage = (e: MessageEvents) => {
566  console.info("The child Worker receives a message" + e.data);
567}
568```
569