通俗易懂讲解什么是Web Workers

519 阅读3分钟

Web Workers

介绍

  • JavaScript 采用单线程模型,所有任务只能在一个线程上完成,一次只能做一件事。
  • Web Workers 是 HTML5 提供的一个javascript多线程解决方案
  • web worker允许主线程创建worker线程,将任务分配在后台运行。这样高延迟,密集型的任务可以由worker线程负担,如此就不冻结用户界面
  • Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

补充:为什么js是单线程?

JavaScript作为浏览器脚本语言,主要用途是与用户互动,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。比如若js同时拥有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除这个节点,那浏览器将不知所措。为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JS脚本创建多个线程,但子线程完全受主线程控制,且不得操作DOM,所以该标准并没有改变JS单线程的本质。

注意点

  • 同源限制

    分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源

  • DOM 限制

    Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以navigator对象和location对象。

  • 通信联系

    Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息通信

  • 脚本限制

    Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

  • 文件限制

    Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

使用

  • 创建在分线程执行的js文件
//worker.js
var onmessage =function (event){ //不能用函数声明
    var upper= event.data;//通过event.data获得发送来的数据
    var result=upper.toUpperCase();  //处理主线程传过来的数据并返回给主线程
    postMessage( result);//将处理好的数据发送会主线程
}

image.png

  • 在主线程中的js中发消息并设置回调
//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");  
//接收worker传过来的已处理的数据
worker.onmessage = function (event) {     
    console.log(event.data);    //HELLO WORLD         
};
//向worker发送数据
worker.postMessage("hello world");    

image.png

image.png

应用练习

编程实现斐波那契数列(Fibonacci sequence)的计算F(0)=0,F(1)=1,..... F(n)=F(n-1)+F(n-2)

  • 直接在主线程

    响应慢,影响了页面的操作

    var fibonacci =function(n) {
        return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
    };
    console.log(fibonacci(48));
    
  • 使用Worker在分线程

    • 主线程
    var worker = new Worker('worker2.js');
    worker.addEventListener('message', function (event) {
        var timer2 = new Date().getTime();
        console.log('结果:' + event.data, '时间:' + timer2, '用时:' + ( timer2 - timer ));
    }, false);
    
    
    var timer = new Date().getTime();
    console.log('开始计算: ', '时间:' + timer);
    setTimeout(function () {
        console.log('定时器函数在计算数列时执行了', '时间:' + new Date().getTime());
    }, 1000);
    
    
    worker.postMessage(40);
    console.log('我在计算数列的时候执行了', '时间:' + new Date().getTime());
    
    
  • 分线程

    var fibonacci =function(n) {
        return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
    };
    
    
    var onmessage = function(event) {
        var n = parseInt(event.data, 10);
        postMessage(fibonacci(n));
    };
    
    

不足

  1. 慢(肯定是比直接在主线程运行慢)

  2. 不能跨域加载JS

  3. worker内代码不能访问DOM(更新UI)

    • 打印分线程的this

    image.png

    • 分线程中的全局对象不在是window,所以在分线程中无法更新界面,无法使用window的方法,比如alert
  4. 不是每个浏览器都支持这个新特性