• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Speeding Up Application Response
2
3This topic provides the following tips for improving your application's response to user input.
4
5- Prevent the main thread from being blocked by non-UI tasks.
6- Reduce the number of components to be refreshed.
7
8## Preventing Main Thread from Being Blocked by Non-UI Tasks
9
10When the application responds to user input, its main thread should execute only UI tasks (such as preparation of data to be displayed and update of visible components). It is recommended that non-UI, time-consuming tasks (such as long-time content loading) be executed through asynchronous tasks or allocated to other threads.
11
12### Using Asynchronous Component Loading
13
14The **\<Image>** component has the asynchronous loading feature enabled by default. When an application loads a batch of local images to be displayed on the page, blank placeholder icons are displayed first, and then replaced by the images when these images have finished loading in other threads. In this way, image loading does not block page display. The following code is recommended only when the image loading takes a short time.
15
16```javascript
17@Entry
18@Component
19struct ImageExample1 {
20  build() {
21    Column() {
22      Row() {
23        Image('resources/base/media/sss001.jpg')
24          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
25        Image('resources/base/media/sss002.jpg')
26          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
27        Image('resources/base/media/sss003.jpg')
28          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
29        Image('resources/base/media/sss004.jpg')
30          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
31      }
32    // Several <Row> containers are omitted here. Each container contains the preceding <Image> components.
33    }
34  }
35}
36```
37
38Recommendation: If it takes a short time to load an image, the benefits of asynchronous loading will be greatly undermined. In this case, change the value of the syncLoad attribute.
39
40```javascript
41@Entry
42@Component
43struct ImageExample1 {
44  build() {
45    Column() {
46      Row() {
47        Image('resources/base/media/sss001.jpg')
48          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
49        Image('resources/base/media/sss002.jpg')
50          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
51        Image('resources/base/media/sss003.jpg')
52          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
53        Image('resources/base/media/sss004.jpg')
54          .border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
55      }
56    // Several <Row> containers are omitted here. Each container contains the preceding <Image> components.
57    }
58  }
59}
60```
61
62### Using TaskPool for Asynchronous Processing
63
64Compared with the worker thread, [TaskPool](https://gitee.com/sqsyqqy/docs/blob/master/en/application-dev/reference/apis/js-apis-taskpool.md) provides the task priority setting and automatic thread pool management mechanism. The following is an example:
65
66```javascript
67import taskpool from '@ohos.taskpool';
68
69@Concurrent
70function computeTask(arr: string[]): string[] {
71  // Simulate a compute-intensive task.
72  let count = 0;
73  while (count < 100000000) {
74    count++;
75  }
76  return arr.reverse();
77}
78
79@Entry
80@Component
81struct AspectRatioExample {
82  @State children: string[] = ['1', '2', '3', '4', '5', '6'];
83
84  aboutToAppear() {
85    this.computeTaskInTaskPool();
86  }
87
88  async computeTaskInTaskPool() {
89    const param = this.children.slice();
90    let task = new taskpool.Task(computeTask, param);
91    // @ts-ignore
92    this.children = await taskpool.execute(task);
93  }
94
95  build() {
96    // Component layout
97  }
98}
99```
100
101### Creating Asynchronous Tasks
102
103The following code shows how to declare a long-running non-UI task as an asynchronous task through **Promise**. This allows the main thread to first focus on providing user feedback and completing the initial render, and then execute the asynchronous task when it is idle. After the asynchronous task is complete, related components are redrawn to refresh the page.
104
105```javascript
106@Entry
107@Component
108struct AspectRatioExample {
109  @State private children: string[] = ['1', '2', '3', '4', '5', '6'];
110  private count: number = undefined;
111
112  aboutToAppear() {
113    this.computeTaskAsync(); // Invoke the asynchronous compute function.
114  }
115
116  // Simulate a compute-intensive task.
117  computeTask() {
118    this.count = 0;
119    while (this.count < 100000000) {
120      this.count++;
121    }
122    this.children = this.children.reverse();
123  }
124
125  computeTaskAsync() {
126    new Promise((resolved, rejected) => {
127      setTimeout(() => {// setTimeout is used to implement asynchronous processing.
128        this.computeTask();
129      }, 1000)
130    })
131  }
132
133  build() {
134    // Component layout
135  }
136}
137```
138
139## Reducing the Number of Components to Be Refreshed
140
141When an application refreshes a page, the number of components to be refreshed must be reduced as much as possible. If this number is too large, the main thread will take a long time to perform measurement and layout. In addition, the **aboutToAppear()** and **aboutToDisappear()** APIs will be called multiple times during the creation and destruction of custom components, increasing the load of the main thread.
142
143### Limiting the Refresh Scope with Containers
144
145Negative example: If a component in a container is included in the **if** condition, changes in the **if** condition result will trigger the creation and destruction of the component. If the container layout is affected in this case, all components in the container are refreshed. As a result, the UI refresh of the main thread takes a long time.
146
147In the following example, the **Text('New Page')** component is controlled by the state variable **isVisible**. When **isVisible** is set to **true**, the component is created. When **isVisible** is set to **false**, the component is destroyed. This means that, when the value of **isVisible** changes, all components in the **\<Stack>** container are refreshed.
148
149```javascript
150@Entry
151@Component
152struct StackExample {
153  @State isVisible : boolean = false;
154
155  build() {
156    Column() {
157      Stack({alignContent: Alignment.Top}) {
158        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
159          .align(Alignment.Center).textAlign(TextAlign.Center);
160
161        // 100 identical <Text> components are omitted here.
162
163        if (this.isVisible) {
164          Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
165            .align(Alignment.Center).textAlign(TextAlign.Center);
166        }
167      }
168      Button("press").onClick(() => {
169        this.isVisible = !(this.isVisible);
170      })
171    }
172  }
173}
174```
175
176Recommendation: For the component controlled by the state variable, add a container to the **if** statement to reduce the refresh scope.
177
178```javascript
179@Entry
180@Component
181struct StackExample {
182  @State isVisible : boolean = false;
183
184  build() {
185    Column() {
186      Stack({alignContent: Alignment.Top}) {
187        Text().width('100%').height('70%').backgroundColor(0xd2cab3)
188          .align(Alignment.Center).textAlign(TextAlign.Center);
189
190        // 100 identical <Text> components are omitted here.
191
192        Stack() {
193          if (this.isVisible) {
194            Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
195              .align(Alignment.Center).textAlign(TextAlign.Center);
196          }
197        }.width('100%').height('70%')
198      }
199      Button("press").onClick(() => {
200        this.isVisible = !(this.isVisible);
201      })
202    }
203  }
204}
205```
206
207### Implementing On-Demand Loading of List Items
208
209Negative example: Each of the 10000 elements in **this.arr** is initialized and loaded. As a result, the execution of the main thread takes a long time.
210
211```javascript
212@Entry
213@Component
214struct MyComponent {
215  @State arr: number[] = Array.from(Array(10000), (v,k) =>k);
216  build() {
217    List() {
218      ForEach(this.arr, (item: number) => {
219        ListItem() {
220          Text(`item value: ${item}`)
221        }
222      }, (item: number) => item.toString())
223    }
224  }
225}
226```
227
228Recommendation: In similar cases, replace **ForEach** with **LazyForEach** so that only visible elements are loaded.
229
230```javascript
231class BasicDataSource implements IDataSource {
232  private listeners: DataChangeListener[] = []
233
234  public totalCount(): number {
235    return 0
236  }
237
238  public getData(index: number): any {
239    return undefined
240  }
241
242  registerDataChangeListener(listener: DataChangeListener): void {
243    if (this.listeners.indexOf(listener) < 0) {
244      console.info('add listener')
245      this.listeners.push(listener)
246    }
247  }
248
249  unregisterDataChangeListener(listener: DataChangeListener): void {
250    const pos = this.listeners.indexOf(listener);
251    if (pos >= 0) {
252      console.info('remove listener')
253      this.listeners.splice(pos, 1)
254    }
255  }
256
257  notifyDataReload(): void {
258    this.listeners.forEach(listener => {
259      listener.onDataReloaded()
260    })
261  }
262
263  notifyDataAdd(index: number): void {
264    this.listeners.forEach(listener => {
265      listener.onDataAdd(index)
266    })
267  }
268
269  notifyDataChange(index: number): void {
270    this.listeners.forEach(listener => {
271      listener.onDataChange(index)
272    })
273  }
274
275  notifyDataDelete(index: number): void {
276    this.listeners.forEach(listener => {
277      listener.onDataDelete(index)
278    })
279  }
280
281  notifyDataMove(from: number, to: number): void {
282    this.listeners.forEach(listener => {
283      listener.onDataMove(from, to)
284    })
285  }
286}
287
288class MyDataSource extends BasicDataSource {
289  private dataArray: string[] = Array.from(Array(10000), (v, k) => k.toString());
290
291  public totalCount(): number {
292    return this.dataArray.length
293  }
294
295  public getData(index: number): any {
296    return this.dataArray[index]
297  }
298
299  public addData(index: number, data: string): void {
300    this.dataArray.splice(index, 0, data)
301    this.notifyDataAdd(index)
302  }
303
304  public pushData(data: string): void {
305    this.dataArray.push(data)
306    this.notifyDataAdd(this.dataArray.length - 1)
307  }
308}
309
310@Entry
311@Component
312struct MyComponent {
313  private data: MyDataSource = new MyDataSource()
314
315  build() {
316    List() {
317      LazyForEach(this.data, (item: string) => {
318        ListItem() {
319            Text(item).fontSize(20).margin({ left: 10 })
320        }
321      }, item => item)
322    }
323  }
324}
325```
326