Mastering Async and Multithread Concurrency in HarmonyOS: From Promises to TaskPool
This article explains HarmonyOS concurrency concepts, covering async patterns with Promise and async/await, the Actor model for multithreading, and practical usage of TaskPool, Worker, and the @Concurrent decorator with code examples to improve app responsiveness and performance.
Concurrency
Concurrency refers to multiple events, tasks, or operations occurring simultaneously or interleaved within a time period.
In computer science, it specifically means the ability of multiple tasks or programs to execute at the same time.
Concurrency can increase system throughput, response speed, and resource utilization, and is essential for multi‑user, multithreaded, and distributed scenarios.
Common concurrency models include multithreading, multiprocessing, multitasking, and coroutines.
01 Concurrency Overview
To improve application response speed and frame rate while preventing time‑consuming tasks from blocking the main thread, HarmonyOS provides two processing strategies: asynchronous concurrency and multithread concurrency.
HarmonyOS asynchronous and multithread concurrency
Async Concurrency
Promise and async/await provide asynchronous concurrency, the standard JavaScript async syntax.
Async code is suspended and resumed later; at any moment only one piece of code runs, suitable for single I/O tasks such as a network request or file read/write without starting a new thread.
Async syntax allows a program to continue executing other operations while waiting for a task to complete.
1. Promise
Promise is an object for handling asynchronous operations, representing a value that may be pending, fulfilled, or rejected.
When the operation finishes, the Promise transitions from pending to fulfilled or rejected and invokes the appropriate callbacks.
Using Promise simplifies asynchronous management and avoids excessive callback nesting.
It can be seen as a proxy representing an operation that will eventually complete.
Promise example
<code>myAsyncFunction(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // simulate success
if (success) {
resolve('提交成功');
} else {
reject('提交失败');
}
}, 1000);
})
}</code> <code>import { BusinessError } from '@kit.BasicServicesKit';
this.myAsyncFunction().then((result: string) => {
console.log(result);
})
.catch((error: BusinessError) => {
console.log(error.message);
})
.finally(() => {
console.log("操作完成");
});</code>then registers a success callback, catch registers a failure callback, and finally registers a completion callback. When the async operation finishes, Promise calls the appropriate callback based on the result.
async/await
async/await is syntactic sugar for Promise, providing a simpler, more readable way to write asynchronous code.
Definition and usage:
An async‑marked function automatically returns a Promise.
<code>async foo() {
// async operation
return "result";
}</code>await keyword
await can only be used inside an async function; it pauses execution until the Promise resolves or rejects.
<code>async myAsyncFunction(): Promise<string> {
const result: string = await new Promise((resolve) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('Hello, world!');
}
}, 3000);
});
console.log(result);
return result;
}
Text(this.message)
.id('Submit')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = this.myAsyncFunction().then((resolve => {
console.info("resolve is: " + resolve);
})).catch((error: BusinessError) => {
console.info("error is: " + error.message);
});
console.info("result is: " + res);
});</code>Using await makes asynchronous code look like synchronous sequential code, avoiding nested callbacks or chained then calls.
Advantages of async/await
Higher readability, resembling synchronous code, easier to understand and maintain.
Errors can be caught with try/catch.
Standard control‑flow statements (loops, conditionals) can organize async execution order.
It relies on Promise under the hood.
Essentially still based on Promise’s asynchronous model.
IO Async Task Development Example
<code>import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
async write(data: string, file: fs.File): Promise<void> {
fs.write(file.fd, data).then((writeLen: number) => {
console.log("write data length is: " + writeLen);
}).catch((error: BusinessError) => {
console.error(`write data failed. Code is ${error.code}, message is ${error.message}`);
})
}
async testWriteFile(): Promise<void> {
let context = getContext() as common.UIAbilityContext;
let filePath: string = context.filesDir + "/logFile.txt";
let file: fs.File = await fs.open(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
this.write("Hello World", file).then(() => {
console.log("write success");
}).catch((error: BusinessError) => {
console.error(`write data failed. Code is ${error.code}, message is ${error.message}`);
})
}</code>03 Multithread Concurrency
Actor Concurrency Model
The Actor model treats each thread as an independent Actor with its own memory; Actors communicate via message passing.
Advantages over shared‑memory models include memory isolation and avoidance of race conditions.
No need to manage locks, improving development efficiency.
ArkTS adopts the Actor model; TaskPool and Worker are built on it.
TaskPool and Worker Implementation Comparison
TaskPool and Worker Applicable Scenarios
Performance‑wise, TaskPool is generally faster, so it is recommended for most cases. TaskPool handles independent tasks without requiring thread lifecycle management; tasks longer than three minutes are automatically reclaimed.
Applicable scenarios:
Tasks exceeding three minutes should use Worker.
Sequences of related synchronous tasks that need persistent handles benefit from Worker.
Frequent task cancellation (e.g., image gallery caching) is better suited to TaskPool.
Long‑running operations such as network requests or database access are appropriate for Worker.
When many small or dispersed tasks exist across modules, TaskPool is preferred.
TaskPool Operation Mechanism
Developers enqueue tasks from the main thread; the system automatically selects suitable worker threads, executes tasks, and returns results.
TaskPool offers a simple API for task execution and cancellation.
System‑wide thread management with dynamic scheduling and load‑balancing saves resources.
Worker Operation Mechanism
Worker threads have independent instances, including infrastructure, objects, and code.
Each Worker incurs memory overhead; the number of Workers should be limited.
Communication between Worker and host thread is based on message passing.
Workers use serialization to exchange commands and data with the host.
TaskPool Notes
@Concurrent Decorator: Validate Concurrent Function
In HarmonyOS, the @Concurrent decorator marks a method to run on a worker thread.
It can be applied to regular methods or callbacks.
Methods annotated with @Concurrent execute without blocking the main thread.
Useful for time‑consuming operations or interactions with other services.
After execution, results can be passed back to the main thread via HarmonyOS inter‑thread communication.
Decorator Usage Example
<code>import taskpool from '@ohos.taskpool';
@Concurrent
function add(num1: number, num2: number): number {
return num1 + num2;
}
async function ConcurrentFunc(): Promise<void> {
try {
let task: taskpool.Task = new taskpool.Task(add, 1, 2);
console.log("taskpool res is:" + await taskpool.execute(task));
} catch (e) {
console.error("taskpool execute error is:" + e);
}
}
@Entry
@Component
struct Index {
@State message: string = 'Submit';
build() {
RelativeContainer() {
Text(this.message)
.id('Submit')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
ConcurrentFunc();
})
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
.height('100%')
.width('100%')
}
}</code>Synchronous Task
Synchronization coordinates multiple async tasks to execute in a defined order or under certain conditions.
Common methods include callbacks, Promise/async chains, inter‑thread messaging, and locks/mutexes.
Using TaskPool to Handle Synchronous Tasks
<code>export default class Handle {
private static singleton : Handle;
public static getInstance() : Handle {
if (!Handle.singleton) {
Handle.singleton = new Handle();
}
return Handle.singleton;
}
public syncGet() { return; }
public static syncSet(num: number) { return; }
}
import taskpool from '@ohos.taskpool';
import Handle from './Handle';
@Concurrent
function func(num: number) {
Handle.syncSet(num);
Handle.getInstance().syncGet();
return true;
}
async function asyncGet() {
let task = new taskpool.Task(func, 1);
let res = await taskpool.execute(task);
console.info(String(res));
}
asyncGet();</code>Summary
The article covered async concurrency in HarmonyOS, including Promise and async/await usage.
It introduced multithread concurrency, comparing TaskPool and Worker capabilities and their suitable scenarios.
Simple examples of using TaskPool were provided.
WeiLi Technology Team
Practicing data-driven principles and believing technology can change the world.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.