浏览器的多线程和单线程

JavaScript 的宿主浏览器只有一个线程运行 JavaScript ,除了 JavaScript 的线程,浏览器中单个页面还有一些其他线程,例如:

  1. UI 线程 负责处理渲染 DOM 元素;
  2. GUI 线程 用于处理与用户交互的逻辑;
  3. 网络线程 用于发送接收 HTTP 请求;
  4. file 线程 用于读取文件;
  5. 定时器线程 处理定时任务等等;

Web Worker 特点

优点

  1. 通过加载一个 JS 文件来进行大量复杂的计算,而不挂起主进程;通过 postMessageonMessage 进行通信;

  2. 可以在 Worker 中通过 importScripts(url) 方法来加载 JavaScript 脚本文件;

  3. 可以使用 setTimeout()clearTimeout()setInterval()clearInterval() 等方法;

  4. 可以使用 XMLHttpRequest 进行异步请求;

  5. 可以访问 navigator 的部分属性;

  6. 可以使用 JavaScript 核心对象;

局限性

  1. 不能跨域加载 JavaScript

  2. Worker 内代码不能访问 DOM

  3. 使用 Web Worker 加载数据没有 JSONPAjax 加载数据高效;

使用介绍

创建子线程

// 判断浏览器是否支持 web worker
if (window.Worker) {
    var worker = new Worker('test.js'); // 创建一个线程,参数为需要执行的 JavaScript 文件
}

向线程传递参数

新的线程的上下文环境跟原宿主环境相对独立的,所以变量作用域不同,如果需要互相读取变量的话需要通过消息发送的方式传输变量

// 数据类型可以是字符串 'test'
worker.postMessage('test'); 

// 数据类型可以是对象 { method: 'echo', args: ['Work'] }
worker.postMessage({ method: 'echo', args: ['Work'] }); 

主线程接受消息

主线程也需要通过监听的方式获取辅线程的消息

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

线程加载脚本

子线程内部也可以通过函数加载其他脚本

importScripts('script1.js', 'script2.js');

关闭线程

// 主线程中关闭子线程
worker.terminate();

// 子线程关闭自身
// Worker 线程中全局对象为 self,代表子线程自身,这时 this 指向 self
self.close();

错误处理

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

  2. Worker 内部也可以监听 error 事件;

worker.onerror(function (event) {
  console.log([
    'ERROR: Line ', event.lineno, ' in ', event.filename, ': ', event.message
  ].join(''));
});

// 或者
worker.addEventListener('error', function (event) {
  // ...
});

// 发送的数据无法序列化成字符串时,会触发 messageerror 事件
Worker.onmessageerror(function (event) {
  // todo
});

适用场景

  1. 并行计算;

  2. ajax 轮询;

  3. 耗时的函数执行;

  4. 数据预处理 / 加载;

案例:多线程实现非阻塞全排列

  1. 什么是全排列:从 n 个不同元素中任取 m (m ≤ n) 个元素,按照一定的顺序排列起来,叫做从 n 个不同元素中取出 m 个元素的一个排列;当 m=n 时所有的排列情况叫全排列;

  2. 代码示例

    HTML
    JavaScript
    JavaScript
    JavaScript
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JavaScript实现全排列</title>
    </head>
    <body>
        <input type="text" id="str" />
        <button onclick="combine()">全排列</button>
        结果是:<div id="result" style="width:500px;height:500px;word-break: break-all;"></div>
    </body>
    <script type="text/JavaScript">
          // 点击按钮向 webworker 线程发送请求  
          function combine() {
              var worker = new Worker('http://127.0.0.1:5500/worker.js');
              worker.postMessage(document.getElementById("str").value);
                  worker.onmessage= function (event) {                        
                    document.getElementById("result").innerHTML  =   event.data ; // 监听 JavaScript 线程的结果
                };
          }
        </script>
    </html>
    
    // worker.js
    
    importScripts('script1.js', 'script2.js');
    
    // 监听主线程的数据请求
    self.onmessage = function (message) {
        var msg = message.data;
        if (msg == "") {
            postMessage("请输入正确的字符串");
        } else {
            postMessage(getGroup(msg.split("")));
        }
    }
    
    // script1.js
    
    // 生成全排列
    function getGroup(data, index = 0, group = []) {
        var need_apply = new Array();
        need_apply.push(data[index]);
    
        for (var i = 0; i < group.length; i++) {
            need_apply.push(group[i] + data[index]);
        }
        group.push.apply(group, need_apply);
    
        if (index + 1 >= data.length) {
            return group;
        } else {
            return getGroup(data, index + 1, group);
        }
    }
    
    // script2.js
    console.log('script2.js');
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 浏览器的多线程和单线程
  2. 2. Web Worker 特点
    1. 2.1. 优点
    2. 2.2. 局限性
  3. 3. 使用介绍
    1. 3.1. 创建子线程
    2. 3.2. 向线程传递参数
    3. 3.3. 主线程接受消息
    4. 3.4. 线程加载脚本
    5. 3.5. 关闭线程
    6. 3.6. 错误处理
  4. 4. 适用场景
  5. 5. 案例:多线程实现非阻塞全排列