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```typescript 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```typescript 41@Entry 42@Component 43struct ImageExample2 { 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](../reference/apis-arkts/js-apis-taskpool.md) provides the task priority setting and automatic thread pool management mechanism. The following is an example: 65 66```typescript 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 AspectRatioExample3 { 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 await taskpool.execute(task); 92 } 93 94 build() { 95 // Component layout 96 } 97} 98``` 99 100### Creating Asynchronous Tasks 101 102The 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. 103 104```typescript 105@Entry 106@Component 107struct AspectRatioExample4 { 108 @State private children: string[] = ['1', '2', '3', '4', '5', '6']; 109 private count: number = 0; 110 111 aboutToAppear() { 112 this.computeTaskAsync(); // Invoke the asynchronous compute function. 113 } 114 115 // Simulate a compute-intensive task. 116 computeTask() { 117 this.count = 0; 118 while (this.count < 100000000) { 119 this.count++; 120 } 121 this.children = this.children.reverse(); 122 } 123 124 computeTaskAsync() { 125 setTimeout(() => {// setTimeout is used to implement asynchronous processing. 126 this.computeTask(); 127 }, 1000) 128 } 129 130 build() { 131 // Component layout 132 } 133} 134``` 135 136## Reducing the Number of Components to Be Refreshed 137 138When 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. 139 140### Limiting the Refresh Scope with Containers 141 142Negative 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. 143 144In 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. 145 146```typescript 147@Entry 148@Component 149struct StackExample5 { 150 @State isVisible : boolean = false; 151 152 build() { 153 Column() { 154 Stack({alignContent: Alignment.Top}) { 155 Text().width('100%').height('70%').backgroundColor(0xd2cab3) 156 .align(Alignment.Center).textAlign(TextAlign.Center); 157 158 // 100 identical <Text> components are omitted here. 159 160 if (this.isVisible) { 161 Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3) 162 .align(Alignment.Center).textAlign(TextAlign.Center); 163 } 164 } 165 Button("press").onClick(() => { 166 this.isVisible = !(this.isVisible); 167 }) 168 } 169 } 170} 171``` 172 173Recommendation: For the component controlled by the state variable, add a container to the **if** statement to reduce the refresh scope. 174 175```typescript 176@Entry 177@Component 178struct StackExample6 { 179 @State isVisible : boolean = false; 180 181 build() { 182 Column() { 183 Stack({alignContent: Alignment.Top}) { 184 Text().width('100%').height('70%').backgroundColor(0xd2cab3) 185 .align(Alignment.Center).textAlign(TextAlign.Center); 186 187 // 100 identical <Text> components are omitted here. 188 189 Stack() { 190 if (this.isVisible) { 191 Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3) 192 .align(Alignment.Center).textAlign(TextAlign.Center); 193 } 194 }.width('100%').height('70%') 195 } 196 Button("press").onClick(() => { 197 this.isVisible = !(this.isVisible); 198 }) 199 } 200 } 201} 202``` 203 204### Implementing On-Demand Loading of List Items 205 206Negative 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. 207 208```typescript 209@Entry 210@Component 211struct MyComponent7 { 212 @State arr: number[] = Array.from(Array<number>(10000), (v,k) =>k); 213 build() { 214 List() { 215 ForEach(this.arr, (item: number) => { 216 ListItem() { 217 Text(`item value: ${item}`) 218 } 219 }, (item: number) => item.toString()) 220 } 221 } 222} 223``` 224 225Recommendation: In similar cases, replace **ForEach** with **LazyForEach** so that only visible elements are loaded. 226 227```typescript 228class BasicDataSource implements IDataSource { 229 private listeners: DataChangeListener[] = [] 230 231 public totalCount(): number { 232 return 0 233 } 234 235 public getData(index: number): string { 236 return '' 237 } 238 239 registerDataChangeListener(listener: DataChangeListener): void { 240 if (this.listeners.indexOf(listener) < 0) { 241 console.info('add listener') 242 this.listeners.push(listener) 243 } 244 } 245 246 unregisterDataChangeListener(listener: DataChangeListener): void { 247 const pos = this.listeners.indexOf(listener); 248 if (pos >= 0) { 249 console.info('remove listener') 250 this.listeners.splice(pos, 1) 251 } 252 } 253 254 notifyDataReload(): void { 255 this.listeners.forEach(listener => { 256 listener.onDataReloaded() 257 }) 258 } 259 260 notifyDataAdd(index: number): void { 261 this.listeners.forEach(listener => { 262 listener.onDataAdd(index) 263 }) 264 } 265 266 notifyDataChange(index: number): void { 267 this.listeners.forEach(listener => { 268 listener.onDataChange(index) 269 }) 270 } 271 272 notifyDataDelete(index: number): void { 273 this.listeners.forEach(listener => { 274 listener.onDataDelete(index) 275 }) 276 } 277 278 notifyDataMove(from: number, to: number): void { 279 this.listeners.forEach(listener => { 280 listener.onDataMove(from, to) 281 }) 282 } 283} 284 285class MyDataSource extends BasicDataSource { 286 private dataArray: string[] = Array.from(Array<number>(10000), (v, k) => k.toString()); 287 288 public totalCount(): number { 289 return this.dataArray.length 290 } 291 292 public getData(index: number): string { 293 return this.dataArray[index] 294 } 295 296 public addData(index: number, data: string): void { 297 this.dataArray.splice(index, 0, data) 298 this.notifyDataAdd(index) 299 } 300 301 public pushData(data: string): void { 302 this.dataArray.push(data) 303 this.notifyDataAdd(this.dataArray.length - 1) 304 } 305} 306 307@Entry 308@Component 309struct MyComponent { 310 private data: MyDataSource = new MyDataSource() 311 312 build() { 313 List() { 314 LazyForEach(this.data, (item: string) => { 315 ListItem() { 316 Text(item).fontSize(20).margin({ left: 10 }) 317 } 318 }, (item:string) => item) 319 } 320 } 321} 322``` 323