Overview of Sendable Objects
In traditional JS engines, there is only one way to optimize the overhead of concurrent object communication: moving the implementation to the native side and reducing costs through the transfer or sharing of Transferable objects. However, this solution falls short of addressing the extensive demand for concurrent object communication. The issue remains unresolved in current JS engine implementations.
ArkTS introduces the concept of Sendable objects, which support pass-by-reference during concurrent communication.
Sendable objects are designed to be shareable across threads, maintaining a consistent reference to the same JS object before and after crossing thread boundaries. If a Sendable object is bound to a native object by calling the N-API, the native object is also shared when the Sendable object is shared. The following figure shows the communication process.

Unlike other ArkTS objects, Sendable objects must have a fixed type at runtime.
When multiple concurrent instances attempt to update Sendable data at the same time, data races occur, such as multithreaded operations on ArkTS shared container. To address data race issues between concurrent instances and manage the timing of multithreaded data processing, ArkTS introduces the mechanisms of asynchronous lock and asynchronous waiting. Additionally, objects can be frozen using the object freezing interface, making them read-only and thereby eliminating the risk of data races.
Sendable objects offer efficient communication between concurrent instances by means of pass by reference. They are generally suitable for scenarios where large custom objects need to be transferred between threads, such as when a child thread reads data from a database and returns it to the main thread. For details about the code implementation, see Transmitting Large Data Across Concurrent Instances.
Basic Concepts
Sendable Protocol
The Sendable protocol defines the Sendable object system and its specifications in ArkTS. Data that complies with the Sendable protocol (referred to as Sendable data) can be passed between concurrent instances in ArkTS.
By default, Sendable data is passed by reference between concurrent instances (including the UI main thread, TaskPool thread, and Worker thread). Pass-by-copy is also supported.
ISendable
The interface ISendable is introduced to the ArkTS common library @arkts.lang. It has no methods or properties. ISendable is the parent type of all Sendable types except for null and undefined. ISendable is mainly used when you want to customize Sendable data structures. The class decorator @Sendable decorator is the syntax sugar for implementing ISendable.
Sendable Class
NOTE
Since API version 11, the @Sendable decorator can be used to verify Sendable classes.
A Sendable class must meet the following requirements:
-
For projects of versions earlier than API version 22, it must be decorated by @Sendable. Since API version 22, in addition to the @Sendable decorator, you can add other custom decorators as required. For details, see Custom Decorators Can Be Added to Sendable Classes.
-
It must meet the Sendable constraints. For details, see Usage Rules and Constraints for Sendable.
Sendable Function
NOTE
Since API version 12, the @Sendable decorator can be used to verify Sendable functions.
For projects with API version 12, to use the @Sendable decorator to verify Sendable functions, you must configure "compatibleSdkVersionStage": "beta3" in the project. Otherwise, the Sendable feature does not take effect. For details, see build-profile.json5.
For projects with API versions later than 12, you can directly use the @Sendable decorator to verify Sendable functions without any other configuration.
A Sendable function must meet the following requirements:
-
It must be decorated by @Sendable.
-
It must meet the Sendable constraints. For details, see Usage Rules and Constraints for Sendable.
Sendable Interface
A Sendable interface must meet the following requirements:
-
It must meet the Sendable constraints. For details, see Usage Rules and Constraints for Sendable.
Sendable Data Types
-
ArkTS basic data types: boolean, number, string, bigint, null, and undefined.
-
ArkTS data type: const enum (constant enumeration).
-
Container types defined in ArkTS (@arkts.collections must be explicitly introduced).
-
Asynchronous lock objects defined in ArkTS (@arkts.utils must be explicitly introduced).
-
Asynchronous waiting objects defined in ArkTS (@arkts.utils must be explicitly introduced).
-
SendableLruCache objects defined in ArkTS (@arkts.utils must be explicitly introduced).
-
Interfaces that inherit from ISendable.
-
Classes decorated by @Sendable.
-
Functions decorated by @Sendable.
-
System objects that integrate Sendable, which are as follows:
-
Elements whose union type data is of the Sendable type.
-
You can also customize Native Sendable objects. You can also customize Native Sendable objects. For details, see Multithreaded Operations with Custom Native Sendable Objects.
NOTE
Built-in JS objects are passed between concurrent instances following the structured clone algorithm, and their cross-thread behavior is pass-by-copy. Therefore, instances of JS built-in objects are not of the Sendable type.
Object literals and array literals are also passed between concurrent instances following the structured cloning algorithm, and their cross-thread behavior is pass-by-copy. Therefore, object literals and array literals are not of the Sendable type.
Example of using const enum type in @Sendable-decorated classes:
// Test.ets
export const enum ModelState {
ACTIVE,
INACTIVE
}
// Index.ets
import { taskpool } from "@kit.ArkTS";
import { ModelState } from "./Test";
@Sendable
class Model {
state: ModelState = ModelState.ACTIVE;
getState() {
console.info("model state is " + this.state);
}
setState(state: ModelState) {
this.state = state;
}
}
@Concurrent
function setModelState(model: Model) {
model.setState(ModelState.INACTIVE);
model.getState();
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State num: number = 0;
build() {
RelativeContainer() {
Text(this.message)
.id('HelloWorld')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(async () => {
let model = new Model();
model.getState();
let task = new taskpool.Task(setModelState, model);
await taskpool.execute(task);
})
}
.height('100%')
.width('100%')
}
}
Implementation Principle of Sendable
To implement pass-by-reference of Sendable data between different concurrent instances, Sendable objects are allocated in a shared heap to achieve memory sharing across concurrent instances.
The shared heap is a process-level heap space. Unlike the local heap of a virtual machine, which can only be accessed by a single concurrent instance, the shared heap can be accessed by all threads. The cross-thread behavior of Sendable objects is pass-by-reference. Therefore, a Sendable object may be referenced by multiple concurrent instances, and its liveness depends on whether any concurrent instance holds a reference to it.
Relationship between the shared heap and local heap

The local heap of each concurrent instance is isolated, whereas the shared heap is a process-level heap that can be shared by all concurrent instances. However, the shared heap cannot reference objects in the local heap.
@Sendable Decorator
The @Sendable decorator declares and verifies Sendable classes and functions.
| @Sendable Decorator | Description |
|---|---|
| Parameters | None. |
| Usage restrictions | This decorator can be used only in .ets files of the stage model. |
| Supported function types | Only regular functions and async functions can be decorated by @Sendable. |
| Class inheritance restrictions | Sendable classes can only inherit from other Sendable classes. Regular classes cannot inherit from Sendable classes. |
| Property type restrictions | 1. The following types are supported: string, number, boolean, bigint, null, undefined, const enum, Sendable class, collections, ArkTSUtils.locks.AsyncLock, ArkTSUtils.SendableLruCache, ArkTSUtils.locks.ConditionVariable, and custom Sendable functions. 2. Closure variables are not allowed, except for top-level Sendable classes and functions. 3. Private properties defined with # are not supported; use private instead. 4. Computed properties are not supported. 5. Type aliases are not supported. |
| Other property restrictions | 1. Member properties must be initialized explicitly. The exclamation mark (!) cannot be used. 2. Adding or deleting properties is not allowed. Modifying properties is allowed, but the type must remain consistent before and after modification. Modifying methods is not supported. |
| Parameter restrictions for decorated functions or class methods | Local variables, parameters, and variables imported through import are allowed. Closure variables are not allowed, except for top-level Sendable classes and functions. In API version 18 and later versions, variables exported from the current file can be accessed. |
| Use scenario | 1. Scenarios where class methods or Sendable functions are used in TaskPool or Worker. 2. Scenarios involving large amounts of object data transmission. The time required for serialization increases with the data volume. After transforming data with Sendable, the efficiency of transmitting 100 KB of data is approximately 20 times higher, and for 1 MB of data, it is about 100 times higher. |
The following is an example of using the decorator on a class:
@Sendable
class SendableTestClass {
desc: string = "sendable: this is SendableTestClass ";
num: number = 5;
printName() {
console.info("sendable: SendableTestClass desc is: " + this.desc);
}
getNum(): number {
return this.num;
}
}
The following is an example of using the decorator on a function:
@Sendable
type SendableFuncType = () => void;
@Sendable
class TopLevelSendableClass {
num: number = 1;
PrintNum() {
console.info("Top level sendable class");
}
}
@Sendable
function TopLevelSendableFunction() {
console.info("Top level sendable function");
}
@Sendable
function SendableTestFunction() {
const topClass = new TopLevelSendableClass(); // Top-level Sendable class.
topClass.PrintNum();
TopLevelSendableFunction(); // Top-level Sendable function.
console.info("Sendable test function");
}
@Sendable
class SendableTestClass {
constructor(func: SendableFuncType) {
this.callback = func;
}
callback: SendableFuncType; // Top-level Sendable function.
CallSendableFunc() {
SendableTestFunction(); // Top-level Sendable function.
}
}
let sendableClass = new SendableTestClass(SendableTestFunction);
sendableClass.callback();
sendableClass.CallSendableFunc();