Web Worker 使用教程

作者: 阮一峰

日期: 2018年7月 8日

一、概述

JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

Web Worker 有以下几个使用注意点。

(1)同源限制

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

(2)DOM 限制

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

(3)通信联系

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

(4)脚本限制

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

(5)文件限制

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

二、基本用法

2.1 主线程

主线程采用new命令,调用Worker()构造函数,新建一个 Worker 线程。


var worker = new Worker('work.js');

Worker()构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。

然后,主线程调用worker.postMessage()方法,向 Worker 发消息。


worker.postMessage('Hello World');
worker.postMessage({method: 'echo', args: ['Work']});

worker.postMessage()方法的参数,就是主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。

接着,主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。


worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
  doSomething();
}

function doSomething() {
  // 执行任务
  worker.postMessage('Work done!');
}

上面代码中,事件对象的data属性可以获取 Worker 发来的数据。

Worker 完成任务以后,主线程就可以把它关掉。


worker.terminate();

2.2 Worker 线程

Worker 线程内部需要有一个监听函数,监听message事件。


self.addEventListener('message', function (e) {
  self.postMessage('You said: ' + e.data);
}, false);

上面代码中,self代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。


// 写法一
this.addEventListener('message', function (e) {
  this.postMessage('You said: ' + e.data);
}, false);

// 写法二
addEventListener('message', function (e) {
  postMessage('You said: ' + e.data);
}, false);

除了使用self.addEventListener()指定监听函数,也可以使用self.onmessage指定。监听函数的参数是一个事件对象,它的data属性包含主线程发来的数据。self.postMessage()方法用来向主线程发送消息。

根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。


self.addEventListener('message', function (e) {
  var data = e.data;
  switch (data.cmd) {
    case 'start':
      self.postMessage('WORKER STARTED: ' + data.msg);
      break;
    case 'stop':
      self.postMessage('WORKER STOPPED: ' + data.msg);
      self.close(); // Terminates the worker.
      break;
    default:
      self.postMessage('Unknown command: ' + data.msg);
  };
}, false);

上面代码中,self.close()用于在 Worker 内部关闭自身。

2.3 Worker 加载脚本

Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()


importScripts('script1.js');

该方法可以同时加载多个脚本。


importScripts('script1.js', 'script2.js');

2.4 错误处理

主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error事件。


worker.onerror(function (event) {
  console.log([
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join(''));
});

// 或者
worker.addEventListener('error', function (event) {
  // ...
});

Worker 内部也可以监听error事件。

2.5 关闭 Worker

使用完毕,为了节省系统资源,必须关闭 Worker。


// 主线程
worker.terminate();

// Worker 线程
self.close();

三、数据通信

前面说过,主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。

主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。下面是一个例子。


// 主线程
var uInt8Array = new Uint8Array(new ArrayBuffer(10));
for (var i = 0; i < uInt8Array.length; ++i) {
  uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8,...]
}
worker.postMessage(uInt8Array);

// Worker 线程
self.onmessage = function (e) {
  var uInt8Array = e.data;
  postMessage('Inside worker.js: uInt8Array.toString() = ' + uInt8Array.toString());
  postMessage('Inside worker.js: uInt8Array.byteLength = ' + uInt8Array.byteLength);
};

但是,拷贝方式发送二进制数据,会造成性能问题。比如,主线程向 Worker 发送一个 500MB 文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。

如果要直接转移数据的控制权,就要使用下面的写法。


// Transferable Objects 格式
worker.postMessage(arrayBuffer, [arrayBuffer]);

// 例子
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);

四、同页面的 Web Worker

通常情况下,Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。


<!DOCTYPE html>
  <body>
    <script id="worker" type="app/worker">
      addEventListener('message', function () {
        postMessage('some message');
      }, false);
    </script>
  </body>
</html>

上面是一段嵌入网页的脚本,注意必须指定<script>标签的type属性是一个浏览器不认识的值,上例是app/worker

然后,读取这一段嵌入页面的脚本,用 Worker 来处理。


var blob = new Blob([document.querySelector('#worker').textContent]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);

worker.onmessage = function (e) {
  // e.data === 'some message'
};

上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。

五、实例:Worker 线程完成轮询

有时,浏览器需要轮询服务器状态,以便第一时间得知状态改变。这个工作可以放在 Worker 里面。


function createWorker(f) {
  var blob = new Blob(['(' + f.toString() +')()']);
  var url = window.URL.createObjectURL(blob);
  var worker = new Worker(url);
  return worker;
}

var pollingWorker = createWorker(function (e) {
  var cache;

  function compare(new, old) { ... };

  setInterval(function () {
    fetch('/my-api-endpoint').then(function (res) {
      var data = res.json();

      if (!compare(data, cache)) {
        cache = data;
        self.postMessage(data);
      }
    })
  }, 1000)
});

pollingWorker.onmessage = function () {
  // render data
}

pollingWorker.postMessage('init');

上面代码中,Worker 每秒钟轮询一次数据,然后跟缓存做比较。如果不一致,就说明服务端有了新的变化,因此就要通知主线程。

六、实例: Worker 新建 Worker

Worker 线程内部还能再新建 Worker 线程(目前只有 Firefox 浏览器支持)。下面的例子是将一个计算密集的任务,分配到10个 Worker。

主线程代码如下。


var worker = new Worker('worker.js');
worker.onmessage = function (event) {
  document.getElementById('result').textContent = event.data;
};

Worker 线程代码如下。


// worker.js

// settings
var num_workers = 10;
var items_per_worker = 1000000;

// start the workers
var result = 0;
var pending_workers = num_workers;
for (var i = 0; i < num_workers; i += 1) {
  var worker = new Worker('core.js');
  worker.postMessage(i * items_per_worker);
  worker.postMessage((i + 1) * items_per_worker);
  worker.onmessage = storeResult;
}

// handle the results
function storeResult(event) {
  result += event.data;
  pending_workers -= 1;
  if (pending_workers <= 0)
    postMessage(result); // finished!
}

上面代码中,Worker 线程内部新建了10个 Worker 线程,并且依次向这10个 Worker 发送消息,告知了计算的起点和终点。计算任务脚本的代码如下。


// core.js
var start;
onmessage = getStart;
function getStart(event) {
  start = event.data;
  onmessage = getEnd;
}

var end;
function getEnd(event) {
  end = event.data;
  onmessage = null;
  work();
}

function work() {
  var result = 0;
  for (var i = start; i < end; i += 1) {
    // perform some complex calculation here
    result += 1;
  }
  postMessage(result);
  close();
}

七、API

7.1 主线程

浏览器原生提供Worker()构造函数,用来供主线程生成 Worker 线程。


var myWorker = new Worker(jsUrl, options);

Worker()构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则会报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。


// 主线程
var myWorker = new Worker('worker.js', { name : 'myWorker' });

// Worker 线程
self.name // myWorker

Worker()构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下。

  • Worker.onerror:指定 error 事件的监听函数。
  • Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
  • Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
  • Worker.postMessage():向 Worker 线程发送消息。
  • Worker.terminate():立即终止 Worker 线程。

7.2 Worker 线程

Web Worker 有自己的全局对象,不是主线程的window,而是一个专门为 Worker 定制的全局对象。因此定义在window上面的对象和方法不是全部都可以使用。

Worker 线程有一些自己的全局属性和方法。

  • self.name: Worker 的名字。该属性只读,由构造函数指定。
  • self.onmessage:指定message事件的监听函数。
  • self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
  • self.close():关闭 Worker 线程。
  • self.postMessage():向产生这个 Worker 线程发送消息。
  • self.importScripts():加载 JS 脚本。

(完)

留言(74条)

感觉学好js,需要的知识面越来越多了,虽然看的不是很懂吧,也了解了些新东西。

赞,想了解下 实际项目上的使用场景!

引用无名的发言:

赞,想了解下 实际项目上的使用场景!

一些消耗性能的事情,例如:
上传大文件的时候,可以用这个搞.

想了解实战时,可以在哪些场景中应用worker

大致就是利用线程消息来实现并行,模拟多线程。可以对比 Service Worker区别一下。

worker.postMessage(i * items_per_worker);
worker.postMessage((i + 1) * items_per_worker);
说好的是多线程,为什么这段代码
必定是上次执行完,切换了worker中的onmessage事件,才执行下一个。

javascript在奇怪的方向上越走越遠,基因決定了js最終要失敗。

不仅可以优化浏览器端的体验,那些使用js开发的web app也大受其益,比如Electron应用。

引用月的发言:

worker.postMessage(i * items_per_worker);
worker.postMessage((i + 1) * items_per_worker);
说好的是多线程,为什么这段代码
必定是上次执行完,切换了worker中的onmessage事件,才执行下一个。

同一个 worker 内是单线程。

在 [六、实例: Worker 新建 Worker] 中的实例,三个文件都放到了一个文件夹下,worker.js 中var worker= new Worker('core.js') 为什么会报错?错误如下:Uncaught ReferenceError: Worker is not defined .难道worker中不能继续new worker ? 还是我哪里错误有误?

@wwmin:

我疏忽了,刚刚确认 Worker 新建 Worker ,目前只有 Firefox 浏览器支持。

引用阮一峰的发言:

@wwmin:

我疏忽了,刚刚确认 Worker 新建 Worker ,目前只有 Firefox 浏览器支持。

17年的时候我试着用过一次,谷歌浏览器也是可以的呀

引用wZi的发言:

一些消耗性能的事情,例如:
上传大文件的时候,可以用这个搞.

上传大文件如果用异步上传的话也没有性能问题吧?

阮老师,在【五、实例:Worker 线程完成轮询】代码例子里,正确的createWorker方法是:
function createWorker(f) {
var blob = new Blob(['(' + f.toString() + ')()']);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
return worker;
}
不然会报错的

@章鱼来:

谢谢指出,我改过来了。

引用js小白的发言:

javascript在奇怪的方向上越走越遠,基因決定了js最終要失敗。

这是6、7年就有的技术。恕我直言你的话就是一坨屎。可悲可笑。

引用月的发言:

worker.postMessage(i * items_per_worker);
worker.postMessage((i + 1) * items_per_worker);
说好的是多线程,为什么这段代码
必定是上次执行完,切换了worker中的onmessage事件,才执行下一个。

我觉得,wenbWorker是主动往主线程传数据,而不是主线程主动去调用它,子线程达到某个要求后才会去主动和主线程通信,这就是区别吧

主进程和worker中的监听api风格不一致觉得好别扭,主进程用onmessage属性赋值,worker用addEventListener函数。这么设计有什么深意吗?统一多好啊

老师你好,最近查阅了一下MDN文档,有些变化,准确的说这个名词应该是复数:web workers。具体有Worker(dedicate worker), Service Worker, Shared Worker等这些,在细节上还是有些差异的,比如GlobalScope,APIs方面。

另外,在worker线程里的navigator和location分别是WorkerNavigator和WorkerLocation的实例对象,并不完全是我们所熟悉的navigator和location。

老师若是有时间可以去重新查阅一下。

引用李帅武的发言:

我觉得,wenbWorker是主动往主线程传数据,而不是主线程主动去调用它,子线程达到某个要求后才会去主动和主线程通信,这就是区别吧

我觉得应该是worker线程中还是同步执行代码,当遇到postMessage()方法的时候,就应该会通知主线程。

学到很多,补充以下。

worker对象它有以下几个全局方法:

最小化的navigator对象,包括onLine、appName、appVersion、userAgent和platform属性。

只读的location属性。

setTimeout()、setInterval()、clearTimeout()和clearTimeout()、clearInterval()方法。

XMLHttpRequest构造函数。

引用恩赐丶解脱丿的发言:

想了解实战时,可以在哪些场景中应用worker

最有意思的用法,做网页游戏,主线程渲染,worker处理数据

在混合app中不能使用吗?我在mui框架中使用的,但是没有效果,这是为什么呢?

引用永康的发言:

上传大文件如果用异步上传的话也没有性能问题吧?

异步是利用浏览器的多线程 但是分片传输的请求创建以及回调的处理还是放在js的主线程 而利用web workers从理论上来说可以瞬间并发请求 以及回调的处理也是互不干扰 个人感觉会有一些性能上的提升 但这种提升不会特别大 个人观点 没有验证

写了一段计数的程序测试js的多线程,发现子线程总是要等主线程运行完之后才会运行,这是什么原因呢?







计数测试JS的多线程



var worker = new Worker("child.js");
for(let i=1;i console.log("主线程计数:" + i);
}



//child.js
for(let i=1;i console.log("子线程计数:" + i);
}

引用总想造轮子的发言:

写了一段计数的程序测试js的多线程,发现子线程总是要等主线程运行完之后才会运行,这是什么原因呢?

计数测试JS的多线程

var worker = new Worker("child.js");
for(let i=1;i
console.log("主线程计数:" + i);
}

//child.js
for(let i=1;i
console.log("子线程计数:" + i);
}

因为 web worker 是相当于后台运行,只有等主线程空闲了才会执行。web worker 设计来就是要让浏览器一直运行良好,如果不让主线程先执行,你web worker 计算量大还是会卡死浏览器。那么有没有web worker 都没有什么区别了

引用js小白的发言:

javascript在奇怪的方向上越走越遠,基因決定了js最終要失敗。

大而全,不如简而精。
ES3、ES5都不错。进入ES6 JavaScript的路就有点儿偏了。后面还要高一年一个版本,es2016,es2017......一个辅助性脚本语言,应该以通俗易懂、方便实用为根本。es6的很多东西些定义的都很抽象,蹩脚。就拿模块化加载来说,我只想说真蹩脚。

2.4 错误处理那部分,应该是
windows.onerror = function (event) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
}

目前chrome,nodejs都已支持worker,在worker创建时,第二个参数type 可以用module,这样线程中导入脚本,可以直接使用 import 或require. 线程使用中需要注意,postmessage不能高频率调用,会导致cpu和内存剧增

new Worker() 出来的子线程要等到主线程结束才能工作,如果主线程不结束,那子线程岂不是永远都运行不了啦

“在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。” 这句话有问题,经过测试,worker 线程必须等待主线程结束才开始。

worker.onerror(function (event) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
});

参数引用错误哟,event => e

# another way for worker listen message

```js


// callback function
onmessage = function (e){
console.log(`\n%c 主线程 e = `, css, e);
// evt.data 接收数据
let url = e.data;
// fetch data
const datas = [];
// decodeURI()
console.log(`%c 主线程 url = `, css, `"${url}"`);
fetch(url)
.then(res => res.json())
.then(
(json) => {
// console.log(`fetched json = \n`, JSON.stringify(json, null, 4));
let userInfos = json.user;
console.log(`%c userInfos = `, css_fd, JSON.stringify(userInfos, null, 4));
datas.push(userInfos);
postMessage(userInfos);
// 发送数据
}
)
.catch(err => log(`fetch error = \n`, err));
};

```

准备用 web workers & indexedDB 搞一个 IM APP, 不知是否适合这种场景?

引用js小白的发言:

javascript在奇怪的方向上越走越遠,基因決定了js最終要失敗。

所以你就是个弟弟,这样的观念导致你永远是个js小白,前端菜鸟

阮老师,在文章开头,关于同源限制,您是这样说的:‘分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。’。
我认为这个说法不正确,应该是:‘分配给 Worker 线程运行的脚本文件,必须与页面文档同源。’。

引用superlu的发言:

阮老师,在文章开头,关于同源限制,您是这样说的:‘分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。’。
我认为这个说法不正确,应该是:‘分配给 Worker 线程运行的脚本文件,必须与页面文档同源。’。

对的,这里很容易误导

阮老师您好
想提几个问题
1. 就您了解,有哪些厂商有在使用web-worker么
2. 最近想通过创建定量(比如4个)的workers做为守护线程,将常用的算法和请求从主线程剥离,但是由于我不太会分析性能,想请问这种思路是否可行呢?

引用jacieron的发言:

目前chrome,nodejs都已支持worker,在worker创建时,第二个参数type 可以用module,这样线程中导入脚本,可以直接使用 import 或require. 线程使用中需要注意,postmessage不能高频率调用,会导致cpu和内存剧增

你测试过吗?
我测试过: 游戏, 我用worker循环处理数据,主线程循环渲染, 性能很好
结论:内存和cpu并不会剧增!

worker 能不能和cpu核绑定?

2.4 错误处理

参数是 event ,而 console.log 里面是 e.lineno e.filename e.message

五、实例:Worker 线程完成轮询

函数 function compare(new, old) { ... } "new" 是不能当参数的

为什么会报错?错误如下:Uncaught ReferenceError: Worker is not defined
原因:file:///D:/selftraining/html5/webworker/my_task.js像这样本地文件夹开头的访问是不行的。
要放在服务器运行才行。
解决方法:
用阮老师的代码,或者拷贝这个项目:https://github.com/mdn/simple-web-worker
用http-server开启本地服务 npm install http-server -g
然后在浏览器访问就行了。

Worker 线程完成轮询,fetch 请求跨域怎么处理,用 http-proxy,会报错 Failed to execute 'fetch' on 'WorkerGlobalScope': Failed to parse URL from,直接写全连接会报跨域问题

引用永康的发言:

上传大文件如果用异步上传的话也没有性能问题吧?

复杂的计算,有些计算可能需要几秒钟才能算出结果的。
还有介绍轮询,轮询是一个持续消耗的过程,如果页面主线程本来就复杂,这时候就能提升体验了。

引用SHR的发言:

2.4 错误处理那部分,应该是
windows.onerror = function (event) {
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''));
}

我当时看时候就觉得奇怪,为什么onerror是一个函数,今天测试,发现是阮老师写错了,onerror是用句柄的方式写的

这个东西说实话,很牛逼,但是用处不是很广,而且编写麻烦,所以我已经把Worker深度封装成js的Thread的类型了,实例化Thread对象调用起来非常方便,以备不时之需!

引用js小白的发言:

javascript在奇怪的方向上越走越遠,基因決定了js最終要失敗。

不然你说你是小白呢,这是早年Chromium 推的,只不过和HTML5 一起标准化了,跟JavaScript有什么关系?

阮老师你真的太菜了,文章不少地方都写错了

一般什么业务需要用到worker分配多线程执行?既然这么做的话,和数据通信的ajax请求相同,又为什么要用worker?那和websocket又有什么区别!

有哪位前辈能详细说说 Worker 线程轮询 吗,或者有没有什么实例。
我不太会用,使用阮老师的代码,但是出现报错,URL.createObjectURL 这里生成的临时blob://路径报错。

Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Failed to parse URL from server.php

有点问题 2.4代码onerror后应有=号

牛是很牛,但感觉吧这玩意还是很少用,处理异步问题还是用的async、await多些(不止我自己)

这个技术,在多个js模块进行数据交互时用起来很方便,比如在websocke通信的onmessage方法中将服务器响应的数据,回调到其它js模块,用这种手法就很爽。

我也初次学到web worker,在web worker的特性上,我有个疑问:
就是js异步放到任务队列上的任务是不是也由浏览后台类似于web worker这样的线程去处理了???

应用场景,例如前端实现远距离分段Dijkstra算法,并行执行多段路径规划

目前实际项目中使用web workers的场景有哪些?

引用麦草CMS的发言:

大而全,不如简而精。
ES3、ES5都不错。进入ES6 JavaScript的路就有点儿偏了。后面还要高一年一个版本,es2016,es2017......一个辅助性脚本语言,应该以通俗易懂、方便实用为根本。es6的很多东西些定义的都很抽象,蹩脚。就拿模块化加载来说,我只想说真蹩脚。

菜就是菜,自己只想呆在舒适区就不要找那么多理由,社会进步首先淘汰的就是你这类人。愚昧、迂腐、无知、自以为是

如果想加快大文件的读取与计算,可不可以创建多个worker文件加快运算速度和效率

做了几年前端,没见有人用过worker呢

函数套函数,worker里面也能创建worker,感觉JS好套娃

在chrome 96.0.4664.45中,子worker使用importScripts会出现变量已经定义
例如
子worker:
let x = 1
importScripts(b.js)

// b.js
let x = 2

抛出异常 x已经被定义。我看mdn上是说 不会被覆盖,但是却抛出异常了。真奇怪

webwork中加载ffmpeg经过webassembly化的js库,并且将一些耗时操作丢到里面去操作,譬如H265软解。

@8mile:

MDN 上的解释:importScripts() 方法将一个或多个脚本同步导入到工作者的作用域中。

注意这里的“作用域”,当前作用域中已经存在,再定义就会出现错误。

引用暴躁男孩的发言:

函数套函数,worker里面也能创建worker,感觉JS好套娃

是个语言都支持嵌套,为什么要针对JS。

最近在使用Monaco-editor的过程中了解到了 Web Worker API,搜到了阮老师这篇文章,Monaco因为语法解析需要耗费大量时间,所以用worker来新开一个线程异步处理返回结果,不影响主线程UI渲染。
楼上好多人说Web Worker 没有使用场景的时候,回过头看看自己的编辑器,是不是会有所顿悟呢。

// main
let init_wk = new Worker("./loader.wk.js")
init_wk.postMessage([1,2,3,4,5,6])

// worker
self.onmessage = message=>{
console.log(message.data,'-'+(new Date).getTime())
}

在火狐中为什么会有两条输出

我项目中所经历的一个应用场景,页面保持一个ws连接,需要对推送过来的数据进行二次处理再进行分发数据到各个页面,如果放主线程中,那么页面请求数据后,每次推送过来数据都需要主线程等待数据处理完成,推送频率高后,会造成页面卡顿,无法点击的问题,后面用sharedworker,在worker里面对数据进行处理,然后再分发到主线程,处理完后,实现了多个标签页共享一个链接,然后页面也流畅了很多

>>2.4 错误处理 实例代码
应该是这个吧。onerror = function(e){}
onerror不是一个函数

Worker构造函数第一个参数为网络脚本或一段js代码通过Object.createObjectURL生成的url,不得为本地脚本。那么这个js远程文件或这段js脚本是用来做耗时操作的吧?比如文章中举出的轮询查状态.

引用嚯嚯嚯的发言:

牛是很牛,但感觉吧这玩意还是很少用,处理异步问题还是用的async、await多些(不止我自己)

实际上不太一样,可以参考mdn里的worker简介,因为js是单线程,所以在执行一个大型计算的时候,cpu被一个任务完全占用,页面会失去响应。
promise,ajax虽然是异步,但还是单线程,按我的理解,就是cpu在多个任务之间不断地切换。worker是真正的多线程,区别就是如果cpu没有余力了就用worker,cpu有余力的情况下async,await就够用了。

在进行大批量数据计算的时候可以采用worker的形式,将计算量放到后台worker当中计算之后返回到主线程,让主线程只负责读,子线程负责写,这样大大优化了性能开销

开发中终于找到了一个业务场景可以用下worker,但却发现一个可以说绕不过去的问题,就是这段【Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。】,无法使用本地脚本如何配合自身的业务呢,是我对这个“本地文件”的理解有误吗?(我的设想:写一个分片上传js,再使用worker执行它),但在 new Worker(src/script/upload.js) 就出错了,是我误解了 worker 的使用吗,有没有大佬指点一下(抱拳

之前项目运用过几次,且效果非常好,有一个场景: 需要使用FileReader同时读取大量的文件,然后发送给服务端。
还有一个最简单的测试场景,当用户拖入txt文本,并解析txt文本的内容,如果用户有上百万的数据,使用该方案,会有意想不到的效果。

Worker 线程中虽然可以使用 XMLHttpRequest 发出 AJAX 请求,但只能进行异步请求,同步请求是不被支持的。

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接