Web Workers:在后台线程执行指定脚本

845 阅读2分钟

使用Web Worker可以在后台线程中运行 JavaScript,线程可以执行任务而不会干扰用户界面。

使用规则

  • worker是使用Worker()构造出的实例对象,在后台线程中运行一个命名的 JavaScript 脚本。
  • worker实例对象可以通过将信息发送到创建它的 JavaScript 代码。
  • worker运行在另一个全局上下文中,而非window对象。
  • worker中不能直接操作 DOM 节点,也不能使用window对象的默认方法和属性。
  • worker和主线程之间,通过postMessage()方法发送各自的数据,使用onmessage事件处理函数来响应数据,数据在message事件的data属性中。
  • worker和主线程之间传递的数据是另外复制的数据。
  • 主线程可以使用实例对象上的terminate()方法立刻终止该worker
  • worker线程内部可以使用close()关闭自身。
  • worker的一个优势在于能够执行处理器密集型的运算而不会阻塞 UI 线程。

应用举例

如下代码所示,在主线程中首先进行了兼容性检验,创建了一个由worker.js执行的Worker。通过myWorker.postMessage()发送消息。通过myWorker.onmessage响应Worker返回的消息。

const a = 1;
const b = 2;

if (window.Worker) {
  const myWorker = new Worker('./worker.js');
  myWorker.postMessage({ a, b });

  myWorker.onmessage = e => {
    console.log('收到worker的信息...');
    console.log(e.data);
  };
}

Worker中,通过onmessage响应主线程发送的消息,经过一些处理后,通过postMessage返回消息到主线程。

// worker.js
onmessage = e => {
  console.log('收到主线程的信息...');
  const { a, b } = e.data;
  const sum = a + b;
  postMessage(sum);
};

从这里我们可以看到,主线程中,onmessagepostMessage()必须挂在worker对象上。而在worker内部,不必这样做,因为worker有自己的作用域。

如下代码所示,在workeronmessage中打印this

onmessage = e => {
  console.log(this);
};

image-20220411142557425

终止 worker

在上述代码中,若在主线程中调用myWorker.terminate(),则不会收到任何消息,因为worker线程刚创建完毕就被终止。

const a = 1;
const b = 2;

if (window.Worker) {
  const myWorker = new Worker('./worker.js');
  myWorker.postMessage({ a, b });
  // 终止myWorker
  myWorker.terminate();

  myWorker.onmessage = e => {
    console.log('收到worker的信息...');
    console.log(e.data);
  };
}

同样,在worker线程中调用close(),主线程也不会有任何响应,因为线程刚启动就被关闭。

close();

onmessage = e => {
  console.log('收到主线程的信息...');
  const { a, b } = e.data;
  const sum = a + b;
  postMessage(sum);
};

共享 worker

主线程:

const a = 1;
const b = 2;

if (window.Worker) {
  const myWorker = new SharedWorker('./worker.js');
  myWorker.port.postMessage({ a, b });

  myWorker.port.onmessage = e => {
    console.log('收到worker的信息...');
    console.log(e.data);
  };
}

worker线程:

onconnect = e => {
  const port = e.ports[0];

  port.onmessage = e => {
    const { a, b } = e.data;
    const sum = a + b;
    port.postMessage(sum);
  };
};

参考: