_

阅读目录

一:web workers的基本原理

我们都知道,我们的javascript采用的是单线程模型,所有的任务都在一个主线程中完成,一次只能执行一个任务,如果有多个任务需要被执行的话,那么后面的任务会依次排队等着,那么这种情况下,如果我们需要处理大量的计算逻辑的时候,那么就会比较耗时,那么用户界面就很有可能出现假死的状态,或者浏览器被直接卡死,这样非常影响用户体验。这个时候我们的web workers就出现了,来解决这样类似的问题。

我们可以把javascrpt单线程模式理解我们日常生活中的快餐收银员可能会更好的理解,我们平时吃快餐依次排队,然后结账,目前只有一个收银员结账,所有的人都要排队依次来,那假如说某个人拿了很多很多菜,收营员需要慢慢的算账到底要收多少钱,那么这个时候一般会要多点时间,那么其他人就在后面排着队等着,等前面的结账完成后,再依次去结账,这样就会很耗时,那假如现在收银员有2个或更多的地方结账的话,我们就可以到其他人少的地方去结账了,这样就可以使速度更快的去完成某个任务,其实现在我们的 web workers 也是这么一种机制,一些复杂业务逻辑,我们的主线程可以把这些任务交给 web workers子线程去处理,子线程处理完成后,会把结果返回给主线程,然后我们的主线程就执行。

web workers的作用:它使用javascript创建workers线程,我们浏览器主线程可以把一些复杂的业务处理逻辑交给worker线程去运行,在我们的主线程运行的同时,我们的worker线程也在后台运行,两者互补干扰。等到worker线程完成计算任务的时候,会再把结果返回给主线程。这样的优点是:一些复杂的计算逻辑,我们可以把它交给worker线程去完成,主线程就会很流畅,不会被阻塞了。

Worker线程一旦创建成功了,就会始终运行了,不会被主线程的运行打断,虽然这样有利于随时响应主线程的通信,但是这也造成了Worker比较耗费资源,在我们编码过程中,可以适当的使用,并且如果使用完成后,我们应该需要关闭。

Web Worker 使用注意如下常见的几点:

1. 同源限制:分配给 Worker线程运行的脚本文件,必须与主线程的脚本文件同源。
2. DOM限制:Worker所在的线程它不能操作window,document这样的对象,也就是说worker线程不能操作dom对象
,但是worker线程可以操作业务逻辑,然后把操作后的结果返回给主线程,主线程再去做相关的DOM操作。

3. 文件限制:Worker线程无法读取本地文件,也就是说不能打开本机的文件系统(如:file://) 这样的,它所加载的脚本,必须来自网络。

Web Worker 浏览器支持程度如下所示:

二:web Workers 的基本用法

1. 创建worker线程方法:

我们在主线程js中调用 new 命令,然后实列化 Worker()构造函数,就可以创建一个Worker线程了,如下代码所示:

  1. var worker = new Worker('work.js');

Worker()构造函数的参数是一个脚本文件,该文件就是Worker线程需要执行的任务,由于Worker不能读取本地文件,所以这个脚本必须来自网络。

如果我们在本地调用 work.js 线程的话,就会报如下错

如上初始化完成后,我们的主线程需要向子线程发送消息,使用 worker.postMessage()方法,向Worker发送消息。如下所示:

worker.postMessage('hello world');

worker.postMessage 方法可以接受任何类型的参数,甚至包括二进制数据。

发送消息完成后,子线程去处理操作,然后把结果返回给主线程,那么主线程通过 worker.onmessage 指定监听函数,接收子线程传送回来的消息,如下代码所示:

  1. worker.onmessage = function(event) {
  2. console.log('接收到的消息为: ' + event.data);
  3. }

如上代码,事件对象的data属性可以获取worker发送回来的消息。

如果我们的worker线程任务完成后,我们的主线程可以把它关闭掉,使用如下代码:

  1. worker.terminate();

2. worker线程

Worker线程内部需要有一个监听函数,监听主线程/其他子线程 发送过来的消息。监听事件为 'message'. 如下代码所示:

  1. self.addEventListener('message', function(e) {
  2. self.postMessage('子线程向主线程发送消息: ' + e.data);
  3. self.close(); // 关闭自身
  4. });

如上代码,self代表子线程本身,也可以为子线程的全局对象。

注意:主线程向子线程发送消息为:worker.postMessage('hello world'); 这样的,但是子线程向主线程发送消息,是如下代码所示:

self.postMessage('子线程向主线程发送消息: ' + e.data);

其实上面的写法 和如下两种写法是等价的,如下代码:

  1. // 写法一
  2. this.addEventListener('message', function(e) {
  3. this.postMessage('子线程向主线程发送消息: ' + e.data);
  4. this.close(); // 关闭自身
  5. });
  6.  
  7. // 写法二
  8. addEventListener('message', function(e) {
  9. postMessage('子线程向主线程发送消息: ' + e.data);
  10. close(); // 关闭自身
  11. });

注意:如果我们使用了 self.addEventListener 来监听函数的话,那么我们也要使用 self.postMessage() 这样的来发送消息,如果我们使用 this.addEventListener 来监听函数的话,那么也应该使用 this.postMessage 来发送消息,如果我们使用如下方法: addEventListener('message', function(e) {}); 来监听函数的话,那么我们就可以使用 postMessage()方法来发送消息的。

3. 了解 importScripts() 方法

如果我们的worker内部需要加载其他的脚本的话,我们可以使用 importScripts() 方法。如下代码所示:

importScripts('a.js');

当然该方法也可以加载多个脚本,如下代码所示:

importScripts('a.js', 'b.js');

4. 错误处理

主线程可以监听Worker线程是否发生错误,如果发生错误,Worker线程会触发主线程的error事件。

  1. // 方法一
  2. worker.onerror(function(e) {
  3. console.log(e);
  4. });
  5.  
  6. // 方法二
  7.  
  8. worker.addEventListener('error', function(e) {
  9. console.log(e);
  10. });

worker线程内部也是可以监听 error 事件的。

5. 关闭线程

如果我们的线程使用完毕后,为了节省系统资源,我们需要关闭线程。如下方法:

  1. // 关闭主线程
  2. worker.terminate();
  3.  
  4. // 关闭子线程
  5. self.close();

三:在webpack中配置 Web Workers

还是和之前一样,配置之前,我们来看下我们项目整个目录架构如下:

  1. |--- web-worker项目
  2. | |--- node_modules # 安装的依赖包
  3. | |--- public
  4. | | |--- images # 存放项目中所有的图片
  5. | | |--- js
  6. | | | |--- main.js # js 的入口文件
  7. | | | |--- test1.worker.js # worker 线程的js文件
  8. | | |--- styles # 存放所有的css样式
  9. | | |--- index.html # html文件
  10. | |--- package.json
  11. | |--- webpack.config.js

1. 在项目中安装 worker-loader 依赖,如下命令所示:

  1. npm install -D worker-loader

2. 在webpack配置中添加如下配置:

  1. module.exports = {
  2. module: {
  3. rules: [
  4. {
  5. test: /\.worker\.js$/, // 以 .worker.js 结尾的文件将被 worker-loader 加载
  6. use: {
  7. loader: 'worker-loader',
  8. options: {
  9. inline: true
  10. // fallback: false
  11. }
  12. }
  13. }
  14. ]
  15. }
  16. }

如上正则匹配的是以 以 .worker.js 结尾的文件将被 worker-loader 加载, 也就是说我们在项目中我们的worker文件名可以叫 test.worker.js 类似这样的名字,或其他的,只要保证 xxx.worker.js 这样的文件名即可。

在上面配置中,设置 inline 属性为 true 将 worker 作为 blob 进行内联;内联模式将额外为浏览器创建 chunk,即使对于不支持内联 worker 的浏览器也是这样的;比如如下运行,我们可以在我们的本地项目下看到有如下这么一个请求:

在开发环境下或正式环境中 我们要如何配置呢?

如果在本地开发中,我们会使用 webpack-dev-server 启动本地调式服务器,如果只有上面的配置的话,我们可以在控制台中会报如下的错误;"Uncaught ReferenceError: window is not defined"; 这样的错误,如下所示:

要解决如上的错误的话,我们需要在我们的webpack配置文件下的out下,加一个属性 globalObject: 'this'; 如下代码:

  1. module.exports = {
  2. output: {
  3. globalObject: 'this'
  4. }
  5. }

比如我现在的webpack配置如下:

  1. module.exports = {
  2. output: {
  3. filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : '[name].js',
  4. // 将输出的文件都放在dist目录下
  5. path: path.resolve(__dirname, 'dist'),
  6. publicPath: '/',
  7. globalObject: 'this'
  8. }
  9. }

然后我们继续运行下 就没有报错了。

首先来看下我们的 public/js/main.js 代码如下:

  1. // 加载css样式
  2. require('../styles/main.styl');
  3.  
  4. import Worker from './test1.worker.js';
  5.  
  6. // 创建worker实列
  7. var worker = new Worker();
  8.  
  9. // 向worker线程发送消息
  10. worker.postMessage('主线程向worker线程发送消息');
  11.  
  12. // 监听worker线程发送回来的消息
  13.  
  14. worker.onmessage = function(e) {
  15. console.log('监听worker线程发送回来的消息如下所示:')
  16. console.log(e);
  17. };

然后我们的 public/js/test1.worker.js(子线程)的代码如下所示:

  1. // 监听消息
  2. onmessage = function(e) {
  3. console.log('监听到的消息为:' + e.data);
  4. }
  5.  
  6. const msg = '工作线程向主线程发送消息';
  7.  
  8. // 发送消息
  9. postMessage(msg);

然后运行结果如下所示:

如上代码,我们首先创建了一个worker实列,如代码:var worker = new Worker(); 然后他就会调用 test1.worker.js 代码,该worker中的代码会首先给主线程发送消息,消息文本为: '工作线程向主线程发送消息'; 然后我们的主线程中会通过 worker.onmessage 事件来监听子线程的消息,因此我们第一次打印出来为如下代码的消息:

  1. worker.onmessage = function(e) {
  2. console.log('监听worker线程发送回来的消息如下所示:')
  3. console.log(e);
  4. };

然后我们的主线程才会向子线程发送消息,如下代码:

  1. // 向worker线程发送消息
  2. worker.postMessage('主线程向worker线程发送消息');

然后 test1.worker.js 代码中的 onmessage 就能监听到消息,如下所示:

  1. // 监听消息
  2. onmessage = function(e) {
  3. console.log('监听到的消息为:' + e.data);
  4. }

最后就会打印出信息如下:"监听到的消息为:主线程向worker线程发送消息"。

四:Web Worker的应用场景

4.1. 使用 web workers 来解决耗时较长的问题

现在我们需要做一个这样的demo,我们在页面中有一个input输入框,用户需要在该输入框中输入数字,然后点击旁边的计算按钮,在后台计算从1到给定数值的总和。如果我们不使用web workers 来解决该问题的话,如下demo代码所示:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>web worker 实列</title>
  6. </head>
  7. <body>
  8. <h1>从1到给定数值的求和</h1>
  9. 输入数值: <input type="text" id="num" />
  10. <button onclick="calculate()">计算</button>
  11.  
  12. <script type="text/javascript">
  13. function calculate() {
  14. var num = parseInt(document.getElementById("num").value, 10);
  15. var result = 0;
  16. // 循环计算求和
  17. for (var i = 0; i <= num; i++) {
  18. result += i;
  19. }
  20. alert('总和魏:' + result + '。');
  21. }
  22. </script>
  23. </body>
  24. </html>

如上代码,然后我们输入 1百亿,然后让计算机去帮我们计算,计算的时间应该要20秒左右的时间,但是在这20秒之前的时间,那么我们的页面就处于卡顿的状态,也就是说什么都不能做,等计算结果出来后,我们就会看到如下弹窗提示结果了,如下所示:

那现在我们需要使用我们的 web workers 来解决该问题,我们希望把这些耗时操作使用 workers去解决,那么主线程就不影响页面假死的状态了,我们首先把index.html 代码改成如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>web worker 实列</title>
  6. </head>
  7. <body>
  8. <h1>从1到给定数值的求和</h1>
  9. 输入数值: <input type="text" id="num" />
  10. <button id="calculate">计算</button>
  11. </body>
  12. </html>

然后在我们的 public/js/main.js 代码如下:

  1. // 加载css样式
  2. require('../styles/main.styl');
  3.  
  4. import Worker from './test1.worker.js';
  5.  
  6. // 创建worker实列
  7. var worker = new Worker();
  8.  
  9. var calDOM = document.getElementById('calculate');
  10. calDOM.addEventListener('click', calculate);
  11.  
  12. function calculate() {
  13. var num = parseInt(document.getElementById("num").value, 10);
  14. // 将我们的数据传递给 worker线程,让我们的worker线程去帮我们做这件事
  15. worker.postMessage(num);
  16. }
  17.  
  18. // 监听worker线程的结果
  19. worker.onmessage = function(e) {
  20. alert('总和值为:' + e.data);
  21. };

public/js/test1.worker.js 代码如下:

  1. // 监听消息
  2. onmessage = function(e) {
  3. var num = e.data;
  4. var result = 0;
  5. for (var i = 0; i <= num; i++) {
  6. result += i;
  7. }
  8. // 把结果发送给主线程
  9. postMessage(result);
  10. }

如上代码我们运行下可以看到,我们点击下计算按钮后,我们使用主线程把该复杂的耗时操作给子线程处理后,我们点击按钮后,我们的页面就可以操作了,因为主线程和worker线程是两个不同的环境,worker线程的不会影响主线程的。因此如果我们需要处理一些耗时操作的话,我们可以使用 web workers线程去处理该问题。

4.2. 实现创建内嵌的worker

如上是在webpack中配置使用 web workers 的使用,我们也可以实现创建内嵌的worker,那么什么是 内嵌的worker呢?首先我们把 webpack 中的如下配置代码注释掉:

  1. module.exports = {
  2. output: {
  3. // globalObject: 'this'
  4. }
  5. }

然后我们运行代码,肯定报错:'Uncaught ReferenceError: window is not defined'。 那么现在我们使用 创建内嵌的worker来解决这样的问题。我们通过 URL.createObjectURL()创建URL对象,可以实现创建内嵌的worker。我们把上面的 test1.worker.js 代码写到一个js文件里面,也就是写到main.js里面去,如下代码:

  1. var myTask = `onmessage = function(e) {
  2. var num = e.data;
  3. var result = 0;
  4. for (var i = 0; i <= num; i++) {
  5. result += i;
  6. }
  7. // 把结果发送给主线程
  8. postMessage(result);
  9. }`;
  10.  
  11. var blob = new Blob([myTask]);
  12. var myWorker = new Worker(window.URL.createObjectURL(blob));
  13.  
  14. // 创建worker实列
  15. // var worker = new Worker();
  16.  
  17. var calDOM = document.getElementById('calculate');
  18. calDOM.addEventListener('click', calculate);
  19.  
  20. function calculate() {
  21. var num = parseInt(document.getElementById("num").value, 10);
  22. // 将我们的数据传递给 worker线程,让我们的worker线程去帮我们做这件事
  23. myWorker.postMessage(num);
  24. }
  25.  
  26. // 监听worker线程的结果
  27. myWorker.onmessage = function(e) {
  28. alert('总和值为:' + e.data);
  29. };

注意:这边只是简单的演示下 web worker 能解决一些耗时操作的问题,如果想要学习更多关于web workers 可以自己google下折腾下。我这边先到此了。也就是说,如果在一些js耗时的代码,我们可以使用子线程来解决类似的问题,这样就不会导致页面被卡死的状态。
web-worker 项目github查看(注意:这只是一个框架,内部没有任何代码,我们可以把上面的代码复制到里面去运行下即可)。

Webpack 下使用 web workers 及 基本原理 和 应用场景的更多相关文章

  1. ES6+Webpack 下使用 Web Worker

    大家都知道 HTML 5 新增了很多 API,其中就包括 Web Worker,在普通的 js 文件上使用 ES5 编写相关代码应该是完全没有问题了,只需要在支持 H5 的浏览器上就能跑起来. 那如果 ...

  2. [翻译]Review——How JavaScript works:The building blocks of Web Workers

    原文地址:https://blog.sessionstack.com/how-javascript-works-the-building-blocks-of-web-workers-5-cases-w ...

  3. 深入web workers (上)

    前段时间,为了优化某个有点复杂的功能,我采用了shared workers + indexDB,构建了一个高性能的多页面共享的服务.由于是第一次真正意义上的运用workers,比以前单纯的学习有更多体 ...

  4. html5 Web Workers

    虽然在JavaScript中有setInterval和setTimeout函数使javaScript看起来好像使多线程执行,单实际上JavaScript使单线程的,一次只能做一件事情(关于JavaSc ...

  5. (92)Wangdao.com_第二十五天_线程机制_H5 Web Workers 分线程任务_事件 Event

    浏览器内核 支撑浏览器运行的最核心的程序 IE 浏览器内核            Trident内核,也是俗称的IE内核Chrome 浏览器内核            统称为 Chromium 内核或 ...

  6. 通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

    Web Workers API - Web API 接口参考 | MDNhttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API ...

  7. JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景

    摘要: 理解Web Workers. 原文:JavaScript是如何工作的:Web Workers的构建块 + 5个使用他们的场景 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...

  8. WijmoJS 使用Web Workers技术,让前端 PDF 导出效率更高效

    概述 Web Workers是一种Web标准技术,允许在后台线程中执行脚本处理. WijmoJS 的2018v3版本引入了Web Workers技术,以便在生成PDF时提高应用程序的运行速度. 一般来 ...

  9. Web Workers文档

    Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法.线程可以执行任务而不干扰用户界面.此外,他们可以使用XMLHttpRequest执行 I/O  (尽管responseXML和 ...

随机推荐

  1. Python连载8-datetime包函数介绍

    一.datetime包(上接连载7内容) 1.函数:datetime (1)用法:输入一个日期,来返回一个datetime类​ (2)格式:datetime.datetime(年,月,日,hour=, ...

  2. Android自动化测试探索(三)Android SDK tools安装、aapt配置以及使用aapt获取apk包名

    Android SDK tools安装 下载连接: https://www.androiddevtools.cn 找到对应mac的版本下载安装即可 AAPT配置 #1. 进入根目录 cd ~ #2. ...

  3. Docker安装MySql-挂载外部数据和配置

    环境 CentOS:7 Docker:1.31.1 MySql:5.7   拷贝mysql配置文件 1.首先创建mysql容器 -p : -e MYSQL\_ROOT\_PASSWORD= -d my ...

  4. NET中级开发工程师职责要求

    NET中级开发工程师岗位职责1.熟练掌握 WebApi, ASP.NET MVC, Entity Framework,.NET CORE等技术,精通JQuery.Vue.Bootstrap等前端开发框 ...

  5. linux下用户权限划分

    场景: 建立一个目录为/devcode,该目录是给开发组用的,也就是只有开发组用户才能进行操作该目录.该组下有成员zhangsan,lisi  步骤: 1.创建用户组,命名dev groupadd d ...

  6. Niginx简单的配置

    #user nobody; #这里的数值不能超过 CPU 的总核数,因为在单个核上部署超过 1 个 Nginx 服务进程并不起到提高性能的作用.worker_processes 2; #nginx进程 ...

  7. app兼容测试选择哪些机型才够全面呢?

  8. ES6_08_Iterator遍历器

    Iterator遍历器: 概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制 作用: 1.为各种数据结构,提供一个统一的.简便的访问接口: 2.使得数据结构的成员能够按某种 ...

  9. oralce中的dual详解 转 http://blog.sina.com.cn/s/blog_a5a24bcb0100zeay.html

    dual是属于sys的只有一个X varchar2(1)列查询虚拟列不会产生逻辑IO========================================================== ...

  10. 下一代工业通信—TSN(时间敏感网络),工业物联网的助推器

    随着工业物联网(IIoT)的兴起和工业4.0的提出,越来越多的设计师.工程师和最终用户关注TSN(Time-Sensitive Networking,时间敏感网络).TSN为以太网提供确定性性能,本质 ...