JavaScript – Web Worker

前言

在上一篇 << 单线程 与 执行机制 >> 中, 我们提到了 Web Worker.

它的诞生是为了解决 JS 主线程执行耗时计算时, 导致 UI 无法及时更新的卡死现象.

它的解决思路是把同步代码异步化. 原本需要 JS 主线程执行的运算, 转交给另一条线程去完成.

这和 setTimeout 是同一个原理. 计数也不是 JS 主线程做的, 而是定时器触发线程做的.

 

参考

阮一峰 – Web Worker 使用教程

MDN – SharedWorker

 

Worker 基本使用

The Problem

假设我们有一个复杂运算 for loop 50亿次 (耗时 5 秒)

document.querySelector('h1').textContent = 'Hello World';
for (let i = 0; i < 5_000_000_000; i++) {} // 需要 5 秒钟
console.log('do something else');

如果我们让 JS 主线程来处理, 那么 UI 渲染就会慢 5 秒钟, 用户迟迟才会看见 Hello World 出现.

New Web Worker

这时我们可以开启 Web Worker, 它会创建另一条线程去处理这个 for loop. 然后我们把后续的代码变成一个 callback.

worker.js

for (let i = 0; i < 5_000_000_000; i++) {} // 需要 5 秒钟
self.postMessage('done'); // 通知主线程, 任务完成
self.close(); // 可以在子线程关闭, 也可以交由主线程关闭.

index.js

document.querySelector('h1').textContent = 'Hello World';
const worker = new Worker('./worker.js', { name: 'worker' });
// callback
worker.addEventListener('message', () => {
  console.log('do something else');
  worker.terminate(); // 关闭子线程
});

通过 addEventListener 注册 callback, 这样原本同步的代码就变成异步了.

Worker 沟通

主线程和子线程 (worker) 的沟通是通过 postMessage 和 addEventListener('message') 完成的.

postMessage 类似于 dispatchEvent 的意思.

主线程和子线程都可以 postMessage 和 listen message, 也都可以关闭子线程.

主线程也能监听子线程的错误

复制代码
// index.ts – main thread
worker.postMessage('message'); // dispatch event to child thread
worker.addEventListener('message', () => {}); // listen event from child thread
worker.addEventListener('error', () => {}); // listen error from child thread
worker.terminate(); // close child thread

// worker.ts – child thread
self.postMessage('done'); // dispatch event to main thread
self.addEventListener('message', () => {}); // listen event from main thread
self.close(); // close child thread (把自己关了)
复制代码

Message Data Type

复制代码
// index.ts
const worker = new Worker('./worker.js', { name: 'worker' });
const file = document.querySelector('input').files[0];
file.arrayBuffer().then(arrayBuffer => {
  worker.postMessage({ value: 'value', buffer: arrayBuffer }, { transfer: [arrayBuffer] });
});

// worker.ts
self.addEventListener('message', event => {
  const { value, buffer } = event.data;
});
复制代码

两个点要注意

1. postMessage 可以传对象, 但是对象会被 deep copy.

2. ArrayBuffer 也会被 copy, 有时候 size 太大 copy 会很慢, 所以 postMessage 有个设置叫 transferable objects

声明 transfer 以后, ArrayBuffer 就转交给 worker 了 (no more copy, use cut instead), 这时主线程就不可以再读取 ArrayBuffer了.

Import Script in Worker

self.importScripts('/imported-script.js');

worker 内可以 import 其它的 script.

imported-script.js 也可以调用 self.postMessage 和主线程沟通.

而 worker 和 imported-script 要沟通则是通过全局变量 e.g. self.value = 'some value'.

SharedWorker

目前支持率不是很好, 以后才研究 TODO...

 

posted @   兴杰  阅读(712)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2021-10-30 ASP.NET Core – Case Style Conversion
2015-10-30 ui router digest 10 time
点击右上角即可分享
微信分享提示