Javascript:Web Worker基础

作者: Lxylona | 来源:发表于2018-02-28 10:51 被阅读38次

    参考资料
    Web Workers
    web worker详解
    Blob对象

    Web Worker分为专属Worker(Dedicated Worker)和共享Worker(Shared Worker)。

    专属Worker与页面是一对一的关系,它们直接链接到对应的页面。

    共享Worker可以由多个窗口共享,只要这些窗口和该共享worder同源。共享Worker通过处于活动中的端口和主线程们进行通信。

    本质上专属Worker也是通过端口和主线程通信的,但是因为双方端口是一致的,所以把这个特性隐藏了。

    本文只讨论专属Worker

    一 Web Worker是什么

    我们知道js是单线程的,这是浏览器的特性决定的,这意味着我们不能用js进行高复杂度的运算,因为很可能会让浏览器进入“假死”状态。
    Web Worker就是来解决这个问题的。它在浏览器后台运行js代码,而不会占用网页的主线程,从而提高网页的性能。

    二 和主线程的通信

    既然Web Worker是一个线程,那么它跟主线程之间必定需要进行线程间通信。
    Web Worker使用postMessage()方法进行通信。看下面的例子:

    // index.js
    var worker = new Worker('/worker.js')
    worker.addEventListener('message', function (e) {
      console.log('Worker said:', e.data)
    }, false)
    worker.postMessage('Hello World') // 把'Hello World'发送给worker
    
    // worker.js
    self.addEventListener('message', function (e) {
        self.postMessage(e.data); // 把'Hello World'发送回给主线程
    }, false)
    
    // addEventListener也可以用onmessage来代替
    // onmessage = function(e) {
    //   var data = e.data;
    //   ...
    // };
    

    如果不需要传递信息,只想要启动worker,可以不发送信息:

    worker.postMessage() // 启动worker.
    

    需要注意的是,通过postMessage(message)传递的message并不是两个线程共享的,而是复制一个副本
    比如,现在我们传递一个对象:

    // index.js
    var worker = new Worker('/worker.js')
    worker.addEventListener('message', function (e) {
      console.log('Worker said:', e.data.a)
      console.log('a of msg is:', msg.a)
    }, false)
    var msg = {
        a: 2
    }
    worker.postMessage(msg)
    
    // Worker.js
    self.addEventListener('message', function (e) {
        e.data.a = 3 // 修改传过来的msg中的a
        self.postMessage(e.data);
    }, false)
    

    输出是这样的:

    Worker said: 3
    a of msg is: 2
    

    说明两个线程的msg并没有指向同一个对象。

    三 关闭worker

    如果worker已经完成了工作,那么就可以关闭这个worker,释放资源了。
    关闭worker的方法有两个。

    1. 在外部执行worker.terminate()
    2. 在worker内部执行self.close()
      第二种方法比较好,因为这样可以防止意外关闭正在运行的worker

    四 Worker的功能

    由于Web Worker是多线程行为,所以功能是受限的,比如,它不能操作DOM。
    Web Worker可以使用的功能有:

    1. navigator对象

    2. location对象(只读)

    3. XMLHttpRequest

    4. setTimeout() / clearTimeout() 和 setInterval() / clearInterval()

    5. Application Cache (AppCache)

    6. 使用 importScripts() 方法导入外部脚本
      importScripts('script1.js', 'script2.js'......);

    7. 生成其他Web Worker
      Worker 可以生成子 Worker。这对于在运行时进一步拆分大任务来说非常重要。但是,子 Worker 还有几点注意事项:

      • 子 Worker 必须托管在与父网页相同的来源中。
      • 子 Worker 中的 URI 应相对于父 Worker 的位置进行解析(与主网页不同)。

     
    Web Worker不可以使用的功能:

    1. DOM
    2. window对象
    3. document对象
    4. parent对象

    五 错误处理

    如果在执行 Worker 时出现错误,就会触发 ErrorEvent。
    三个可以帮你找出错误的实用属性:

    1. filename - 导致错误的 Worker 脚本的名称
    2. lineno - 出现错误的行号
    3. message - 有关错误的实用说明。
      可以这样处理错误:
    <!-- index.html -->
    
    <output id="error" style="color: red;"></output>
    <output id="result"></output>
    
    <script>
      function onError(e) { // 处理错误
        document.getElementById('error').textContent = [
          'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join('');
      }
    
      function onMsg(e) { // 处理信息
        document.getElementById('result').textContent = e.data;
      }
    
      var worker = new Worker('workerWithError.js');
      worker.addEventListener('message', onMsg, false);
      worker.addEventListener('error', onError, false);
      worker.postMessage(); // 启动Worker
    </script>
    
    // workerWithError.js
    
    self.addEventListener('message', function(e) {
      postMessage(1/x); // x没有定义,抛出错误
    };
    

    六 安全限制

    1. Chrome本地访问限制

    由于 Google Chrome 浏览器的安全限制,Worker 无法在最新版浏览器中本地运行(例如通过 file://)。
    要通过 file:// 方案运行您的应用,请使用 --allow-file-access-from-files 标记设置来运行 Chrome 浏览器。
    请注意:不推荐使用此标记设置来运行您的主浏览器。此标记设置仅供测试用,请勿用于常规浏览。

    其他浏览器不存在相同的限制。

    2. 同源限制

    只能调用同源的Worker,即host相同,协议相同

    七 Worker可以做什么

    计算密集型和I/O密集型的操作都很适合用Web Worker来完成:

    • 预先抓取和/或缓存数据以便稍后使用
    • 突出显示代码语法或其他实时文本格式
    • 拼写检查程序
    • 分析视频或音频数据
    • 背景 I/O 或网络服务轮询
    • 处理较大数组或超大 JSON 响应
    • <canvas> 中的图片过滤
    • 更新本地网络数据库中的多行内容
      ......

    深入了解Web Worker请戳这篇: web worker详解

    八 内联Worker

    有时候没有必要为一个Worker单独创建一个文件,那么可以使用Blob对象来写成内联的Worker:

    // 该blob的内容为创建的worker的代码
    var blob = new Blob([ "onmessage = function(e) { postMessage('msg from worker'); }"])
    // 创建一个url指向该blob
    var blobURL = window.URL.createObjectURL(blob)
    var worker = new Worker(blobURL) // 创建一个worker,Worker接受一个url作为参数
    worker.onmessage = function (e) {
        console.log(e.data)
    }
    worker.postMessage('inline worker!')
    

    我们可以创建一个函数来专门把特性的函数转化为blob并返回一个url让我们使用:

    function fn2workerURL (fn) {
        var blob = new Blob(['(' + fn.toString() + ')()'], {
            type: 'application/javascript'
        })
        return URL.createObjectURL(blob) // 返回一个 blob URL
    }
    function fnInWorker () {
        onmessage = function (e) {
            postMessage(e.data)
        }
    }
    var worker = new Worker(fn2workerURL(fnInWorker))
    worker.onmessage = function (e) {
        console.log(e.data)
    }
    

    相关文章

      网友评论

        本文标题:Javascript:Web Worker基础

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