解析Web Workers

详解Web Workres理念

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和通道属性总是为空)。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序 (反之亦然)

在worker线程中你可以运行任何你喜欢的代码,不过有一些例外情况。不能直接操作DOM

  • 数据传递通过消息机制进行——双方都使用postMessage()方法发送各自的消息,使用onmessage事件处理函数来响应消息(消息被包含在Message事件的data属性中)。这个过程中数据并不是被共享而是被复制

  • 需运行在同源的页面,可以使用XMLHttpRequest 进行网络I/O, 所以本地也需要开启一个服务器。这里推荐npm anywhere

  • 在主线程中使用时,onmessage和postMessage() 必须挂在worker对象上,而在worker中使用时不用这样做。原因是,在worker内部,worker是有效的全局作用域。

  • 终止worker,主线程myWorker.terminate(); work线程直接调用close()

main.js

if (window.Worker) { /
    var myWorker = new Worker("worker.js");

    myWorker.postMessage(['test1','test2']); //发送数据给work.js

    myWorker.onmessage = function(e) { //接收来自work的数据
        console.log(e.data);
    };
}

work.js

onmessage = function(e) {
  var mainData = e.data;
  postMessage('workDate');
}

共享worker

生成一个共享worker var myWorker = new SharedWorker(‘worker.js’);

  • 与一个共享worker通信必须通过端口对象——一个确切的打开的端口供脚本与worker通信(在专用worker中这一部分是隐式进行的)

  • 在使用start()方法打开端口连接时,如果父级线程和worker线程需要双向通信,那么它们都需要调用start()方法。

myWorker.port.start();  // 父级线程中的调用
port.start(); // worker线程中的调用, 假设port变量代表一个端口
  • 共享worker中消息的接收和发送

main.js

myWorker.port.postMessage('myworker');

work.js

onconnect = function(e) {
  var port = e.ports[0];

  port.onmessage = function(e) {
    var workerResult = e.data;
    port.postMessage('workjs');
  }
}

worker中数据的接收与发送:详细介绍

在主页面与 worker 之间传递的数据是通过拷贝,而不是共享来完成的。传递给 worker 的对象需要经过序列化,接下来在另一端还需要反序列化。页面与 worker 不会共享同一个实例,最终的结果就是在每次通信结束时生成了数据的一个副本。大部分浏览器使用结构化拷贝来实现该特性。

传输 JSON 的高级方式和创建一个交换系统

如果你需要传输非常复杂的数据,还要同时在主页与 Worker 内调用多个方法,那么可以考虑创建一个类似下面的系统。

在主线程创建一个QueryableWorker的类,它接收worker的url、一个默认侦听函数、和一个错误处理函数作为参数,这个类将会记录所有的侦听的列表并且帮助我们与worker进行通信。代码如下:

function QueryableWorker(url, defaultListener, onError) {
    var instance = this,
        worker = new Worker(url),
        listeners = {};

    this.defaultListener = defaultListener || function() {};

    if (onError) {worker.onerror = onError;}

    this.postMessage = function(message) {
        worker.postMessage(message);
    }

    this.terminate = function() {
        worker.terminate();
    }

    this.addListeners = function(name, listener) {
            listeners[name] = listener;
          }

      this.removeListeners = function(name) {
            delete listeners[name];
      }

      /* 
  This functions takes at least one argument, the method name we want to query.
  Then we can pass in the arguments that the method needs.
 */
        this.sendQuery = function() {
            if (arguments.length < 1) {
                 throw new TypeError('QueryableWorker.sendQuery takes at least one argument'); 
                 return;
            }
            worker.postMessage({
                'queryMethod': arguments[0],
                'queryArguments': Array.prototype.slice.call(arguments, 1)
            });
        }


        worker.onmessage = function(event) {
    if (event.data instanceof Object &&
        event.data.hasOwnProperty('queryMethodListener') &&
        event.data.hasOwnProperty('queryMethodArguments')) {
        listeners[event.data.queryMethodListener].apply(instance, event.data.queryMethodArguments);
    } else {
        this.defaultListener.call(instance, event.data);
    }
}
}

workjs代码如下

var queryableFunctions = {
    getDifference: function(a, b) {
        reply('printStuff', a - b);
    },
    waitSomeTime: function() {
        setTimeout(function() {
            reply('doAlert', 3, 'seconds');
        }, 3000);
    }
}

function reply() {
    if (arguments.length < 1) { 
        throw new TypeError('reply - takes at least one argument'); 
        return; 
    } 
    postMessage({ 
        queryMethodListener: arguments[0], 
        queryMethodArguments: Array.prototype.slice.call(arguments, 1) 
    });
}

/* This method is called when main page calls QueryWorker's postMessage method directly*/
function defaultReply(message) {
    // do something
}


onmessage = function(event) {
    if (event.data instanceof Object &&
        event.data.hasOwnProperty('queryMethod') &&
        event.data.hasOwnProperty('queryMethodArguments')) {
        queryableFunctions[event.data.queryMethod]
            .apply(self, event.data.queryMethodArguments);
    } else {
        defaultReply(event.data);
    }
}

其它类型的worker

除了专用和共享的web worker,还有一些其它类型的worker:

  • ServiceWorkers (服务worker)一般作为web应用程序、浏览器和网络(如果可用)之前的代理服务器。它们旨在(除开其他方面)创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动并更新驻留在服务器上的资源。他们还将允许访问推送通知和后台同步API。
  • Chrome Workers 是一种仅适用于firefox的worker。如果您正在开发附加组件,希望在扩展程序中使用worker且有在你的worker中访问 js-ctypes 的权限,你可以使用Chrome Workers。详情请参阅ChromeWorker。
  • Audio Workers (音频worker)使得在web worker上下文中直接完成脚本化音频处理成为可能。

猜你喜欢

转载自blog.csdn.net/wkyseo/article/details/77884572