-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
87 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
--- | ||
title: 如何拆分long task | ||
date: 2024-07-04 11:36:59 | ||
tags: typescript 优化 | ||
categories: | ||
--- | ||
|
||
## 前言 | ||
|
||
前段时间实现了一个使用 opencvjs 将印花图与 mask 图原图进行正片叠底的效果,印花使用四方连续的形式拼接,大概效果如下图所示 | ||
|
||
![Untitled](0.png) | ||
|
||
测试反馈说生成图时**操作会变得卡顿**,于是便开始着手优化了。 | ||
|
||
## 找到问题 | ||
|
||
打开 performance 录下操作,发现有长任务需要优化。 | ||
|
||
![Untitled](1.png) | ||
|
||
可以发现有一个比较长的任务,我们看看调用栈分析一下哪个函数时间比较长。从图上可以发现整个 task 主要耗时的函数有 getSourceMatAndMaskMat 和 mergeImage 两个。查看一下 render 函数 | ||
|
||
![Untitled](2.png) | ||
|
||
## 拆分 long task | ||
|
||
对于主线程中的长任务可以使用在 `Promise` 中调用 `setTimeout` 的方式让出主线程。 | ||
|
||
```tsx | ||
export function yieldToMain() { | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, 0); | ||
}); | ||
} | ||
``` | ||
|
||
在两个函数之间加上 yieldToMain | ||
|
||
![Untitled](3.png) | ||
|
||
再录一次操作发现顺利拆分了 | ||
|
||
![Untitled](4.png) | ||
|
||
那么继续看看 getSourceMatAndMaskMat 这个函数里面哪些需要拆出来的 | ||
|
||
## 拆分 for 循环中的 long task | ||
|
||
![Untitled](5.png) | ||
|
||
可以看到 getSourceMatAntMaskMat 主要耗时是在 loopRect 这个循环中,看看 loopRect 做了什么 | ||
|
||
![Untitled](6.png) | ||
|
||
主要是做了遍历区域像素的操作,就算是 100x100 的遍历也要遍历 10000 次,我们可以试一下将长循环拆分成多个批次来进行,接下来我们试一下改造 loopRect 函数来拆分一下。 | ||
|
||
![Untitled](7.png) | ||
|
||
看看效果 | ||
|
||
![Untitled](8.png) | ||
|
||
可以看出,long task 被成功拆分了,从原先一整个 task 超过**200ms**的任务,变成了一个个任务块,最大的任务块也就 40ms 左右,也就是在生成图的过程中是不会影响用户其他操作的。 | ||
|
||
## 一些补充 | ||
|
||
### 尽量不要使用匿名函数 | ||
|
||
在函数编写中,尽量不要使用匿名函数来创建函数,因为这会使你在查找函数栈时变得复杂 | ||
|
||
![Untitled](9.png) | ||
|
||
从图上很难直观得看出这是个什么函数,到底做了什么。 | ||
|
||
### 函数封装 | ||
|
||
每一个功能应该拆分出一个函数出来,每个函数要尽量只做一个事情,拆分成函数才能更好地拆分 task。如何拆分函数可以看看[代码整洁之道](https://book.douban.com/subject/4199741/)的第三章 | ||
|
||
## 结语 | ||
|
||
合理利用 chrome performance 板块,我们可以将一个个 long task 给切分成一个个 short task,减少我们因为长任务导致的操作卡顿。 | ||
|
||
## 参考 | ||
|
||
- [1] [https://web.dev/articles/optimize-long-tasks?hl=zh-cn#async-await](https://web.dev/articles/optimize-long-tasks?hl=zh-cn#async-await) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.