web worker实际使用

2,569 阅读2分钟

web worker实际使用

web worker的好处

相信大家都比较熟悉,比如:

  • 为js创造多线程环境,一些 计算量大的 耗时的 任务,可以用web worker单独开线程去跑,不会影响到主线程的运行
    • 这样主线程(ui交互渲染等)就不会被阻塞,页面流畅

实际如何使用呢?

首先要理解一个区别,主线程和子线程是分别独立,不要混在一起去理解 web worker.png

上一段代码,方便理解:

// 文件的目录结构:
myFile
├── index.html
└── poorWorker.js (穷人打工仔,worker,干活的人)

要测试的话,需要起一个本地的服务器,具体做法:
1. 先下一个全局包 npm i -g http-server
2. 在终端执行命令 hs   (注意当前的path,要在 myFile 路径下)
    - hs的作用是:会起一个服务,读当前目录的 ./index.html

这是主线程:index.html

<script>
const worker = new Worker('./poorWorker.js');
worker.postMessage("我是主进程, 我是boss,我传给worker线程消息: 赶紧干活!!");
worker.onmessage = msg => {
  console.log(msg)
  console.log("我是主进程,我是boss,接收的子线程的数据:", msg.data);
  worker.terminate(); // 关闭主线程
};
worker.onerror = e => {
  console.log(e)
};
</script>

这是子线程:poorWorker.js

// importScripts('./hello2.js', 'http://xxx/a.js'); // 此处可以指出多脚本, 加载的脚本不支持跨域!!

// self 代表子线程自身,即子线程的全局对象。
// self可以换成this或不写,也可以实现执行。此处用self用于区分子线程worker

// 监听主线程传过来的信息
self.onmessage = e => {
  console.log(e)
  console.log('我是子线程, 我在工作, 我是worker, 我收到主线程传来的信息:', e.data)
  // do something
}

// setTimeout非必须,此处模拟,worker干了个耗时的活,2s后告诉主线程(老板)活已经干完ok了。
setTimeout(() => {
  // 发送信息给主线程
  self.postMessage('我是子线程, 我在工作, 我是worker, 工作已经完成 ok')
  closeSon()
}, 2000)

// 关闭worker线程
function closeSon () {
  return self.close()
}

打印效果如下:

webWorkerLog.png

骚操作写法介绍(更推荐上面的写法)

正常情况,推荐上面的写法:把worker部分,单独分离成一个.js文件。

但强行把worker部分,不分离成.js,强行写在index.html内,也可以做到

这是 index.html

<script>
// 此处把上面的poorWorker.js放到data中
const data = `
  // importScripts('./hello2.js', 'http://xxx/a.js'); // 此处可以指出多脚本, 加载的脚本不支持跨域!!

  // self 代表子线程自身,即子线程的全局对象。
  // self可以换成this或不写,也可以实现执行。此处用self用于区分子线程worker

  // 监听主线程传过来的信息
  self.onmessage = e => {
    console.log(e)
    console.log('我是子线程, 我在工作, 我是worker, 我收到主线程传来的信息:', e.data)
    // do something
  }

  // setTimeout非必须,此处模拟,worker干了个耗时的活,2s后告诉主线程(老板)活已经干完ok了。
  setTimeout(() => {
    // 发送信息给主线程
    self.postMessage('我是子线程, 我在工作, 我是worker, 工作已经完成 ok')
    closeSon()
  }, 2000)

  // 关闭worker线程
  function closeSon () {
    return self.close()
  }
`;
const blob = new Blob([data]); // 把poorWorker.js的内容转成二进制
const url = window.URL.createObjectURL(blob); // 把二进制转成一个本地链接
const worker = new Worker(url); // 利用了new Worker()只接受url的特点

// ... 后面的代码同上
worker.postMessage("我是主进程, 我是boss,我传给worker线程消息: 赶紧干活!!"); 
worker.onmessage = msg => { 
    console.log(msg) 
    console.log("我是主进程,我是boss,接收的子线程的数据:", msg.data); 
    worker.terminate(); // 关闭主线程
}; 
worker.onerror = e => { 
    console.log(e) 
};
</script>

最后(断开)

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。

但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。


码字不易,点赞鼓励