
最近在看 useWorker 库的时候发现该 hook 只需传入函数即可运行,虽然说 worker 不经常使用,但是就酱紫直接用不需要一个 js 文件承接?
来看看 MDN 中如何创建一个 Worker
var myWorker = new Worker('worker.js');
|
再看看 useWorker
如何使用
useWorker Usage
import React from 'react'; import { useWorker } from '@koale/useworker';
const numbers = [...Array(5000000)].map((e) => ~~(Math.random() * 1000000)); const sortNumbers = (nums) => nums.sort();
const Example = () => { const [sortWorker] = useWorker(sortNumbers);
const runSort = async () => { const result = await sortWorker(numbers); console.log(result); };
return ( <button type="button" onClick={runSort}> Run Sort </button> ); };
|
源码解析与最简实现
Demo
最简实现
提取了下 useWorker 的大致逻辑,简化实现了下~主要看下核心逻辑,”凭空”创建 worker
const createWWorker = (fn) => { console.log('worker run'); let promise = {}; const jobRunner = (options) => (e) => { const [userFuncArgs] = e.data; return Promise.resolve(options.fn(...userFuncArgs)) .then((result) => { postMessage(['SUCCESS', result]); }) .catch((error) => { postMessage(['ERROR', error]); }); };
const blobCode = ` onmessage=(${jobRunner})({ fn: (${fn}), }) `;
const blob = new Blob([blobCode], { type: 'text/javascript' });
const jsUrl = URL.createObjectURL(blob);
const worker = new Worker(jsUrl);
worker.addEventListener('message', (e) => { const [status, result] = e.data;
switch (status) { case 'SUCCESS': { promise.resolve(result); break; } default: promise.reject(result); break; }
if (jsUrl) { worker.terminate(); URL.revokeObjectURL(jsUrl); promise = {}; } });
const runWorker = (...restArgs) => new Promise((resolve, reject) => { promise.resolve = resolve; promise.reject = reject; worker.postMessage([restArgs]); });
return runWorker; };
const worker = createWWorker(bubbleSort); const result = await worker(bigNumbers);
|
const blobCode = ` onmessage=(${jobRunner})({ fn: (${fn}), }) `;
const blob = new Blob([blobCode], { type: 'text/javascript' });
const jsUrl = URL.createObjectURL(blob);
const worker = new Worker(jsUrl);
|
第一段看起来可能比较疑惑,函数 toString 后放入到 script 里也可以运行?试试就逝世~复制下面代码到控制台。
好吧,script type=”text/javascript
“ 忘记这个了,函数 toString 后,比如闭包啊 this 指向等,可能都会有点问题,建议为纯函数~
const script = document.createElement('script'); const script = document.createElement('script'); script.innerText = `const test = 333; document.addEventListener('click', () => { console.log(test); })`; document.body.appendChild(script);
|
通过生成 Blob 创建资源 Url
第一段生成 Blob 流,再通过流来创建 URL 当作 worker 的文件,思路清晰我是菜 B ~还得学~
const blob = new Blob([blobCode], { type: 'text/javascript' });
const jsUrl = URL.createObjectURL(blob);
|
区分 Web Socket、Web Worker 和 Service Worker
可能接触不多,往往会弄混或遗忘这些方法~简单描述下~
Web Socket
WebSocket是基于 TCP 的一个全双工通信协议,HTML5 的新特性。
在此之前,客户端和服务端通信的时候,在客户端发送请求之后,服务端才能有所应答。如果服务器有新数据的话不能主动发至客户端,只能客户端进行:
- 轮询:客户端不断发送新请求给服务端,服务端在应答一次后就断开连接,需不断发起新请求和新连接,造成资源的浪费。
- 长轮询:客户端发送请求后,不关闭连接,等服务端有期望数据返回后关闭连接。弊端在于服务端需要保持连接,造成资源浪费
而 WebSocket 的好处就是在于服务器可以主动发送资源到客户端,且只需一次连接,减少资源损耗。连接后,服务端可以不断的发送数据给客户端,之间无需再连接。
Web Worker
由于 JavaScript 为单线程,在计算一些密集型或高延性的任务时,会影响到整个页面的运行,WebWorker 应声而出。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
引用自阮一峰老师的文章《Web Worker 使用教程》
Service Worker
Service Worker 实际上是浏览器与服务器之间的代理服务器,它最大的特点是在页面注册并成功安装后,运行于浏览器后台,不受页面刷新的影响,可以拦截作用域范围内所有的 Http 请求。

- 安装(Installing)
在浏览器加载了运用 Service Worker 的页面时,Service Worker 的 JS 文件也会被保存下来,然后运行安装,下载相应缓存。安装完成后,就会激活 Service Worker(Activated)
- 激活(Activated)
激活 Service Worker 后,Service Worker开始控制页面后台,当您刷新页面或者发送请求的时候,Service Worker 会对请求进行拦截,然后进行相应的操作(一般是看是否命中缓存,如果命中,就直接返回缓存,不需要再发送请求)
Service Worker在浏览器运行的时候,每隔一段时间,它会在后台尝试重新下载Service Worker的JS文件,只要新的JS文件和旧的有一点不相同,那么就会重新安装-激活。BTW,Service Worker也是Web Work的一种。