前一阵子开发的项目 pptx 导入
, 因为本身的代码问题,引发了个性能问题,一个 40p 的 pptx 文件,转换成 json 数据,大概要耗时 60s+ ,虽而后面发现是某个使用频率很是高的函数内部,用了 new Function 构造函数
形成的(因此这里顺便提醒一下,若是你很在意几毫秒的差距的话,建议谨慎使用哈),可是在优化的过程当中,一度怀疑是性能达到了瓶颈,因此尝试了使用 web worker 去优化,因为是文件,通常内容都比较大,发现 web worker 在传值这块占用了大部分的时间,因此想开这篇来详细聊聊.javascript
关于 web worker 的基本用于以及传值方式,网上以及有一大堆介绍了,这里就不赘述了,这里咱们重点来看一下同一个文件用两种方式来传值,会有多大的差异,这边随意从电脑里面找了一个 96MB 的 PSD 文件来测试.前端
主线程java
fetch('./case.psd').then(file => { return file.blob(); }) .then(blob => { return new Promise(resolve => { let fileReader = new FileReader(); fileReader.onload = e => { resolve(e.target.result); } fileReader.readAsArrayBuffer(blob); }) }) .then(buf => { let worker = new Worker('1.js'); console.time('计算时间'); worker.postMessage(buf); worker.onmessage = e => { console.timeEnd('计算时间'); } })
worker(子)线程, 这里为了不没必要要的因素干扰,worker 线程里面什么也不作,在收到消息后,直接 post 一个消息回去git
self.onmessage = e => { postMessage(0); }
这边我直接用 FileReader 的 readAsArrayBuffer,读出来是一个长度为 96,138,230 的字符串,长度大概 0.96 亿, 耗时大概 70ms 左右(同一个台电脑取 10 次平均值,下同)github
咱们稍微改一下上面主线程的代码,改用 转移数据
的方式web
- worker.postMessage(buf); + worker.postMessage(buf, [buf]);
一样的数据, 耗时大概 17ms 左右,这 17ms 好像是个固定值,我尝试换了个 800MB+ 的文件和一个里面啥都没有的空文本文件,大概都是这个时间.json
fetch('./case.psd').then(file => { return file.blob(); }) .then(blob => { return new Promise(resolve => { let fileReader = new FileReader(); fileReader.onload = e => { resolve(e.target.result); } fileReader.readAsText(blob); }) }) .then(str => { console.log(str.length); let worker = new Worker('1.js'); console.time('计算时间'); worker.postMessage(str); worker.onmessage = e => { console.timeEnd('计算时间'); } })
这里咱们改用 FileReader 的 readAsText,读出来是一个长度为 95,855,954 的字符串,长度大概 0.95 亿, 耗时大概 118ms 左右,一样我换了上面那个里面啥都没有的空文本文件,耗时也是 17ms 左右.函数
那咱们试试用 readAsDataURL 看看读出来的数据要多久post
fetch('./case.psd').then(file => { return file.blob(); }) .then(blob => { return new Promise(resolve => { let fileReader = new FileReader(); fileReader.onload = e => { resolve(e.target.result); } fileReader.readAsDataURL(blob); }) }) .then(str => { console.log(str.length); let worker = new Worker('1.js'); console.time('计算时间'); worker.postMessage(str); worker.onmessage = e => { console.timeEnd('计算时间'); } })
读出来是一个长度为 128,184,345 的字符串,长度大概 1,28 亿, 耗时大概 85ms 左右(虽然字符串长度更长,可是耗时却更短)性能
以上耗时,均为主线成向 worker 线程单向传递数据的耗时.
关于转移的缺点, 网上也是有不少的, 这里也就不啰嗦了, 总结一句就是数据没法同时在2个线程上使用.
另外我的以为若是是普通的数据,为了转移而去转换成 Transferable objects
的话, 大部分状况下是划不来的, 由于你须要在花在编码解码上的时间,会比直接传递花的时间多.
另外, 若是你是要用子线程处理图片的话, ImageBitmap
格式 配合最近新鲜出炉的 OffscreenCanvas
也许是不错的选择.前提是你不须要考虑兼容性问题.
咱们40人的前端团队常年招兵买马中,在厦门的和想来厦门的童鞋们,不要吝惜你的简历,使劲砸过来 邮箱:nuoya@gaoding.com
, 期待你一块儿来稿事