🚪🏃‍♀️ webworker 入门

739 阅读4分钟

概述

Javascript是单线程模型,所有任务在一个线程上完成且一次只能处理一个任务,单线程是为了防止DOM混乱。

webworker的作用就是为JS创建多个线程,允许在主线程之外创建Woker线程(叫做子线程),在主线程运行的同时,Worker线程也在后台(一个新的js环境WorkerGlobalScope)运行,两者互不干扰,Worker完成计算任务之后,再将计算结果返回给主线程。一些计算复杂、延迟高的任务由Worker线程来负担,主线程(通常指UI线程)就可以比较流畅不被阻塞了。

目前除了IE基本所有浏览器都支持Web Worker。

与EventLoop的区别

EventLoop

浏览器线程包括 UI线程(渲染页面)、JS引擎线程(处理js)、GUI事件线程(用户交互)、http传输线程(xhr请求)、定时触发线程(定时器)等。上述浏览器的多个线程并不是真正的多线程,实际上都是有JS线程来处理程序。

  • UI线程 与 JS引擎线程 互斥,JS阻塞页面加载
  • JS引擎线程 与 GUI线程 异步
  • JS引擎线程 与 http传输线程 异步
  • JS引擎线程 与 定时器线程 异步

同步任务是立即执行的任务,直接进入到主线程中执行;异步任务(xhr请求、setTimeout等)会通过EventQueue进行协调。主线程内的任务执行完成为空后,会去读区Event Queue中的任务并推入煮线程执行,这个不断重复的过程也就是EventLoop。

WebWorker

WebWorker 是真正意义上的多线程,只是创建子线程收到主线程的控制。

webworker的应用场景

大计算量的代码

  1. js单线程会发生阻塞。
  2. 计时器事件不准。 将这部分计算代码放到worker线程,不会导致主线程被刮起。

webworker的使用

基本用法

主线程

  1. 创建 worker 线程
/**
 * new Worker(jsURL[, options])
 * @param jsUrl 表示worker将要执行的脚本URL
 * @param options (可选)
 * @options type: 'classic', // 可选值为 classic 或 module ,默认classic
 * @options credentials: 'omit',
 * @options name 设置 worker的名称 DedicatedWorkerGlobalScope 全局对象的name属性。可以通过 _self.name 访问
 */
const myWorker = new Worker('worker.js')
}
  1. 主线程向worker线程发送消息
myWorker.postMessage('hello myWorker');
myWorker.postMessage({ method: 'print', message: 'hello myWorker' });
  1. 主线程接受worker线程发来的消息
myWorker.onmessage = function(e) {
    console.log('Message received from worker is ', e.data);
}
  1. 错误处理
myWorker.onerror(function (event) {
  console.log([
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join(''));
});
  1. 关闭worker线程
myWorker.terminate();

worker线程

  1. worker线程接受消息
onmessage = function(e) {
    console.log('Worker: Message received from main script', e.data.message);
}
  1. worker线程向主线程发送消息
onmessage = function(e) {
    console.log('Message received from main script', e.data.message);
    console.log('Posting message back to main script');
    postMessage('Message from worker');
}
  1. worker加载脚本
importScripts('subWorker.js');
  1. 关闭worker线程
self.close();

同页面的webworker

上述情况为引入外部js文件,也可以载入与主线程再同一个文件的代码。

  1. toString() 将JS函数转化成字符串
  2. new Blob(stringFun) 将worker脚本代码专程二进制对象。
  3. 将二进制对象生成URL。
  4. new Worker(URL) 创建worker线程。
// index.js
const workerBlock = function(e) {
    console.log('Message received from main script', e.data.message);
    console.log('Posting message back to main script');
    postMessage('Message from worker');
}

const blob = new Blob([`onmessage=${workerBlock.toString()}`], { "type": "text/javascript" });
const url = window.URL.createObjectURL(blob);
const myWorker = new Worker(url);

myWorker.postMessage('hello myWorker');
myWorker.postMessage({ method: 'print', message: 'hello myWorker' });

myWorker.onmessage = function(e) {
    console.log('Message received from worker is ', e.data);
}

worker API

主线程

  1. onerror 指定 error 事件监听函数。
  2. onmessage 指定 message 事件的监听函数,发送过来的数据可以通过$event.data访问。
  3. onmessageerror 指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时会触发这个事件。
  4. postMessage 向 worker 线程发送消息。
  5. terminate 立即终止 worker 线程。

worker 线程

  1. name (属性, 只读) worker 的名字,有new Worker指定。
  2. onmessage 指定 message 事件的监听函数。
  3. onmessageerror 指定 messageerror 事件的监听函数。
  4. close 关闭 worker 线程。
  5. postMessage 向创建该 worker 线程的主程序发送消息。
  6. importScripts 加载JS脚本。

worker 的全局对象 WorkerGlobalScope

WorkerGlobalScope 是 worker 的全局对象,包含JS全局对象的属性,例如JSON等,还有window的一些属性,以及XMLHttpRequest等。

webworker的注意事项

  1. 脚本必须来自网络且与主线程脚本同源,无法加载本地文件系统(file://
  2. 不能访问DOM;不能访问document、window、parent等对象;可以访问navigator、location(只读)、XMLHttpRequest、setTimeout等浏览器API。
  3. 不能执行alert()、confirm()方法。
  4. 主线程和worker线程不在同一个上下文环境,不能直接通信必须通过消息完成。

webpack下使用webworker

  1. 安装 worker-loader
npm i -D worker-loader
  1. 解析worker文件
module.exports = {
    chainWebpack: config => {
        // GraphQL Loader
        config.module
            .rule('worker-loader')
            .test(/\.worker\.js$/)
            .use('worker-loader')
            .loader('worker-loader')
            .end()
    }
}
  1. 使用 worker.js
import Worker from './file.worker.js';

const worker = new Worker()

参考文档

Web Worker 使用教程