如何拆分一个long task

395 阅读2分钟

前言

前段时间实现了一个使用opencvjs将印花图与mask图原图进行正片叠底的效果,印花使用四方连续的形式拼接,大概效果如下图所示

Untitled.png

测试反馈说生成图时操作会变得卡顿,于是便开始着手优化了。

找到问题

打开performance录下操作,发现有长任务需要优化。

Untitled 1.png

可以发现有一个比较长的任务,我们看看调用栈分析一下哪个函数时间比较长。从图上可以发现整个task主要耗时的函数有getSourceMatAndMaskMat和mergeImage两个。查看一下render函数

Untitled 2.png

拆分long task

对于主线程中的长任务可以使用在 Promise 中调用 setTimeout 的方式让出主线程。

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。如何拆分函数可以看看代码整洁之道的第三章

结语

合理利用chrome performance板块,我们可以将一个个long task给切分成一个个比较小的任务块,减少我们因为长任务导致的操作卡顿。

参考