美文网首页JsWeb前端之路让前端飞
Javascript 基础夯实 —— 使用 webWorker

Javascript 基础夯实 —— 使用 webWorker

作者: ac68882199a1 | 来源:发表于2017-09-10 20:59 被阅读48次

    当我们开始学习 javascript 的时候,我们就知道 js 其实是单线程的,所以当我们在浏览器中运行某些耗时算法或者阻塞线程的代码时,浏览器就会出现卡顿的现象

    然而浏览器的单个页面却拥有多个线程,比如渲染界面线程、浏览器事件触发线程、http 请求线程、事件轮询处理线程

    如果我们能够将一部分代码放在一个新的线程中执行,比如 http 请求的方法、需要大量计算耗时较多的方法,既能够保证页面对用户及时响应,又不会阻塞页面

    这在以前是不可能的,但是现在,H5 为我们提供了一个方法 —— webWorker

    什么是 webWorker

    webWorker 是浏览器为我们提供的一个可以再浏览器后台开启一个新的线程的 API,使得运行在浏览器中的 js 有了多线程的能力。但是这并不意味这 js 本身就支持多线程

    webWorker 有两种类型,一种是只能在当前页面使用的 webworker,另一种是可以再多个页面之间共享线程的 webWorker,前者随着当前页面关闭而关闭,而后者在同域的前提下,可以被多个页面访问

    webWorker 的创建与使用

    // webWorker 是在主线程中通过传入一个 js 文件的路径来实现的,它返回一个 webWorker 的实例对象,该对象是主线程与该线程通信的桥梁
    let worker = new Woker ('webWorker.js')
    

    webWorker 在主线程和子线程之间实现通信的方法有两个:

    // 监听一个线程向另一个发送的消息并执行指定方法
    // 回调方法接受一个 event 参数,event.data 为接受到的数据
    onmessage = (event) => {}
    
    // 一个线程向另一个线程发送消息
    postMessage(data)
    

    实际使用时可以像下面这样使用:

    /**
     * 主线程
     */
    
    let worker = new Worker ('worker.js')
    worker.onmessage = (e) => {
      console.log(e.data) // I post a message to main thread
    }
    worker.postMessage('main thread got a message')
    
    /**
     * 子线程 worker.js
     */
    onmessage = (e) => {
        console.log(e.data) // main thread got a message
    }
    postMessage('I post a message to main thread')
    

    终止 webWorker

    // 在主线程中终止
    worker.terminate()
    
    // 在子线程中终止自身
    self.close()
    

    错误监听

    当子线程发生错误时,可以在主线程中监听到该错误

    worker.addEventListener('error', (e) => {
        console.error(e.filename) // 导致错误的 worker 脚本名称
        console.error(e.message) // 错误信息
        console.error(e.lineno) // 错误行号
    })
    

    引入其他脚本

    // 引入一个脚本
    importScripts('xxx.js');
    
    // 引入多个脚本
    importScripts('aaa.js', 'bbb.js', 'ccc.js');
    

    importScripts 会按顺序加载每一个脚本,当有任何失败或者错误时,会抛出 SYNTAX_ERR 异常

    共享线程的使用

    主线程中创建一个共享线程

    let shareWorker = new SharedWorker ('shareWorker.js')
    
    shareWorker.port.start()
    shareWorker.port.postMessage('...')
    shareWorker.port.onmessage = (e) => {...}
    

    子线程 shareWorker.js

    onconnect = (e) => {
        let port = e.ports[0]
        port.addEventListener('message', (e) => {
        console.log(e.data[0])
        port.postMessage(...);
      });
      port.start();
    }
    

    postMseeage 方法

    postMseeage 传递数据的过程其实是一个值拷贝的过程,会现将数据 JSON.stringify 之后再 JSON.parse

    postMseeage 也可以传送二进制数据,但是当数据过大时,由于值拷贝,浏览器会再生成一个该文件的拷贝,这样可能会引起浏览器性能的问题

    所以当传输较大数据时,可以直接将数据转移给另一个线程,而不进行值拷贝,只是这样会导致原线程无法再使用这些数据,也能够防止多个线程同时修改的情况发生,这叫做零拷贝

    // 指定传输的所有数据都是零拷贝
    let data = new ArrayBuffer(64)
    worker.postMessage(data, [data])
    
    // 指定数据中的某个属性零拷贝
    let obj = {a: 1, b: 2, c: 3}
    worker.postMessage(obj, [obj.a, obj.c])
    

    需要注意的是,通过 webWorker 创建的线程的运行环境中没有全局对象 window,也无法访问 DOM / BOM 对象,所以他只能用来执行纯粹的 javascript 计算
    当然,他也可以获取到部分浏览器提供的 API,如:
    XMLHttpRequest
    navigator
    location (read only)
    setTimeout(), clearTimeout(), setInterval(), clearInterval()
    Promise 等等
    还有哪些,小伙伴们可以自己去尝试发掘(可以将 self 打印出来看看)

    扫码关注前端周记公众号

    相关文章

      网友评论

        本文标题:Javascript 基础夯实 —— 使用 webWorker

        本文链接:https://www.haomeiwen.com/subject/xevtsxtx.html