阅读 261

工作者线程 Web Worker

这是我参与新手入门的第一篇文章。

一、工作者线程 Web Worker 简介

JavaScript 语言采用的是单线程模型,单线程就意味着不能像多线程语言那样把工作委托给独立的线程或进程去做。即所有任务都在一个线程内完成。简单 的说 Web Worker 可以使 js 拥有 "多线程"。当然 webworker并没有改变js单线程的本质,webworker多线程指的是浏览器多线程,因为浏览器可以提供多个js引擎的实例,每个实例可以独立的运行相应的程序,但在每个js引擎实例中js脚本还是在单线程的执行,每个实例就相当于一个webworker

如何使用 ?

主线程采用new命令,新建一个 Worker 线程。

let worker = new Worker('worker.js');
复制代码

Worker()接收的参数是一个js文件,这个文件就是 Worker 线程所要处理的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。

主线程调用worker.postMessage()方法,向 Worker 发消息。

worker.postMessage('webworker');
worker.postMessage({fn: 'worker', data: ['Worker']});
复制代码

worker.postMessage()方法的参数,就是主线程传递给 Worker 的数据。主线程可以通过worker.onmessage 指定监听函数,接收子线程发回来的消息。


worker.onmessage =  (event) => {
  console.log('收到子线程的消息' + event.data);
  foo();
}

function foo() {
  // 开始执行任务
  worker.postMessage('执行完毕 ');
}
复制代码

event对象的data 属性可以获取Worker发来的数据。

  • Worker 线程

    Worker 线程内部需要有一个监听函数,监听message事件。

self.addEventListener('message', function (e) {
  self.postMessage('传递消息: ' + e.data);
}, false);
复制代码

上面代码是子线程自身,self.postMessage 可以向主线程传递消息。

  • Worker 加载脚本

Worker 可以使用 importScripts()

importScripts('worker1.js');
// 也支持多个脚本
importScripts('worker1.js','worker2.js');
复制代码
  • 监听错误
worker.addEventListener('error',(event)=>{
    // ...
})
复制代码
  • 关闭Worker

Worker 线程一旦新建成功,就会始终运行,这样有利于随时响应主线程的通信。

使用完毕,为了节省系统资源,必须关闭Worker

三、举例

假设我们有一些非常耗时的操作,又不能影响页面的运行,这时我们就可以想到使用webworker
先不使用线程webWorker看下执行斐波那契所需的时间。

function Fib1(n) {
  if (n == 1 || n == 2) {
    return 1;
  }
  return Fib1(n - 1) + Fib1(n - 2);
}
console.time('执行时间');
Fib1(43)
console.timeEnd('执行时间');
复制代码

image.png
这是执行一次斐波那契所需的时间,大约四秒钟。看下执行三次所需 多长时间。

function Fib1(n) {
  if (n == 1 || n == 2) {
    return 1;
  }
  return Fib1(n - 1) + Fib1(n - 2);
}
console.time('执行时间');
Fib1(43)
Fib1(43)
Fib1(43)
console.timeEnd('执行时间');
复制代码

image.png
可以看到三次斐波那契数列执行的时间是约10秒钟。接下来我们让webworker出场。

  • 主线程代码
let worker = new Worker('workerjs_1.js');
let worker = new Worker('workerjs_2.js');
let worker = new Worker('workerjs_3.js');
worker.onmessage =  (event) => {
    //...
}
复制代码
  • Worke线程(需注意脚本必须来源网络)
//  三个线程都一样的代码就拿出一个了
function Fib1(n) {
  if (n == 1 || n == 2) {
    return 1;
  }
  return Fib1(n - 1) + Fib1(n - 2);
}
console.time('执行时间');
Fib1(43)
console.timeEnd('执行时间');

self.addEventListener('message', function (e) {
  console.log(e.data);
  self.postMessage('传递数据 : ' + e.data);
  self.close()
}, false);
复制代码

image.png
如上图可以看到每个Worker 的执行事件都约为4秒(gif 不太会录制,其实是一起执行的),也就是说我们通过分发三个worker 使得原来的10 秒 变为了4秒,整整减少2分之一还多。如果遇到比较耗时的操作我们可以考虑使用worker 帮我们处理。

四、使用场景
  • 加密数据:在加解密很多数据的时候,这会非常耗费计算资源,导致UI线程无响应,使用worker线程可以让用户更加无缝的操作UI。如在断点上传生成文件hash值时,这时便是利用 worker 的好时机。
  • 大数据处理:数据过大的话排序,过滤等操作会十分耗费时间,这时使用worker来进行,不占用主线程。
  • ......
五丶总结

使用webworke大多场景还是浪费时间的操作,使用完毕一定要关闭,不然会浪费cpu资源,十分消耗性能。

参考资料 《javaScript高级设计程序第四版》

文章分类
前端
文章标签