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