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 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