没有单独的 Javascript 文件的 Web 工作者?

IT技术 javascript web-worker
2021-02-02 02:46:24

据我所知,网络工作者需要写在一个单独的 JavaScript 文件中,并像这样调用:

new Worker('longrunning.js')

我正在使用闭包编译器来组合和缩小我所有的 JavaScript 源代码,而且我不想让我的工作人员在单独的文件中进行分发。有没有办法做到这一点?

new Worker(function() {
    //Long-running work here
});

鉴于一流的函数对 JavaScript 如此重要,为什么执行后台工作的标准方法必须从 Web 服务器加载整个其他 JavaScript 文件?

6个回答

http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers

如果您想即时创建您的工作程序脚本,或者创建一个独立的页面而不必创建单独的工作程序文件,该怎么办?使用 Blob(),您可以通过将工作程序代码的 URL 句柄创建为字符串,将工作程序“内联”在与主要逻辑相同的 HTML 文件中


BLOB 内联工作线程的完整示例:

<!DOCTYPE html>
<script id="worker1" type="javascript/worker">
  // This script won't be parsed by JS engines because its type is javascript/worker.
  self.onmessage = function(e) {
    self.postMessage('msg from worker');
  };
  // Rest of your worker code goes here.
</script>
<script>
  var blob = new Blob([
    document.querySelector('#worker1').textContent
  ], { type: "text/javascript" })

  // Note: window.webkitURL.createObjectURL() in Chrome 10+.
  var worker = new Worker(window.URL.createObjectURL(blob));
  worker.onmessage = function(e) {
    console.log("Received: " + e.data);
  }
  worker.postMessage("hello"); // Start the worker.
</script>

@albanx - 您是否愿意至少说一下您使用哪个深奥的浏览器,哪个挂起?这个演示对你有用吗?ie.microsoft.com/testdrive/Graphics/WorkerFountains/...
2021-03-25 02:46:24
BlobBuiler现在已弃用改用Blob目前在最新的 Firefox/WebKit/Opera 和 IE10 中受支持,请参阅旧浏览器的兼容性表
2021-03-29 02:46:24
谷歌浏览器唯一的解决方案,似乎 Firefox 10 会支持它,我不知道其他浏览器
2021-04-04 02:46:24
斑点的构造可能在IE10支持,但你仍然可以不通过javascript来通过它的网络工作者(甚至在IE11):connect.microsoft.com/IE/feedback/details/801810/...
2021-04-04 02:46:24
@albanx - 什么测试?网上已经有十亿个演示页面,这些页面显示线程多年来不会挂断浏览器。
2021-04-11 02:46:24

在 HTML 中嵌入 web worker 代码的 html5rocks 解决方案相当糟糕。
一团转义的 JavaScript-as-a-string 也好不到哪里去,尤其是因为它使工作流程复杂化(闭包编译器不能对字符串进行操作)。

我个人非常喜欢 toString 方法,但是@dan-man那个正则表达式!

我的首选方法:

// Build a worker from an anonymous function body
var blobURL = URL.createObjectURL( new Blob([ '(',

function(){
    //Long-running work here
}.toString(),

')()' ], { type: 'application/javascript' } ) ),

worker = new Worker( blobURL );

// Won't be needing this anymore
URL.revokeObjectURL( blobURL );

Support是这三个表的交集:

但是,这不适用于SharedWorker,因为 URL 必须完全匹配,即使可选的“name”参数匹配。对于 SharedWorker,您需要一个单独的 JavaScript 文件。


2015 更新 - ServiceWorker 奇点到来

现在有一种更强大的方法可以解决这个问题。同样,将工作代码存储为函数(而不是静态字符串)并使用 .toString() 进行转换,然后将代码插入到您选择的静态 URL 下的 CacheStorage 中。

// Post code from window to ServiceWorker...
navigator.serviceWorker.controller.postMessage(
 [ '/my_workers/worker1.js', '(' + workerFunction1.toString() + ')()' ]
);

// Insert via ServiceWorker.onmessage. Or directly once window.caches is exposed
caches.open( 'myCache' ).then( function( cache )
{
 cache.put( '/my_workers/worker1.js',
  new Response( workerScript, { headers: {'content-type':'application/javascript'}})
 );
});

有两种可能的回退。ObjectURL 如上所述,或者更无缝地,将一个真正的JavaScript 文件放在 /my_workers/worker1.js

这种方法的优点是:

  1. 也可以支持 SharedWorkers。
  2. 选项卡可以在固定地址共享单个缓存副本。blob 方法为每个选项卡增加随机的 objectURL。
该标准不保证 Function.prototype.toString() 将函数体作为字符串返回。您可能应该在答案中添加警告。
2021-03-18 02:46:24
你能详细说明这个解决方案,它是如何工作的?worker1.js 是什么?它是一个单独的js文件吗?我正在尝试使用它,但无法使其工作。具体来说,我正在尝试使其适用于 SharedWorker
2021-03-22 02:46:24
要是你能把它包装成一个有用的函数就好了!
2021-03-27 02:46:24
此解决方案的浏览器兼容性如何?
2021-04-08 02:46:24
@ Ben Dilts:浏览器兼容性看起来就像通过 babel 运行您的代码:babeljs.io/repl
2021-04-10 02:46:24

您可以创建单个 JavaScript 文件,该文件知道其执行上下文并且可以充当父脚本和工作器。让我们从这样一个文件的基本结构开始:

(function(global) {
    var is_worker = !this.document;
    var script_path = is_worker ? null : (function() {
        // append random number and time to ID
        var id = (Math.random()+''+(+new Date)).substring(2);
        document.write('<script id="wts' + id + '"></script>');
        return document.getElementById('wts' + id).
            previousSibling.src;
    })();
    function msg_parent(e) {
        // event handler for parent -> worker messages
    }
    function msg_worker(e) {
        // event handler for worker -> parent messages
    }
    function new_worker() {
        var w = new Worker(script_path);
        w.addEventListener('message', msg_worker, false);
        return w;
    }
    if (is_worker)
        global.addEventListener('message', msg_parent, false);

    // put the rest of your library here
    // to spawn a worker, use new_worker()
})(this);

如您所见,该脚本包含父级和工作人员观点的所有代码,检查其自己的单个实例是否是带有!document. 有点笨拙的script_path计算用于准确计算脚本相对于父页面的路径,因为提供给的路径new Worker是相对于父页面的,而不是相对于脚本的。

我认为使用 'typeof importScripts !== null' 的测试可以判断脚本是否在工作范围内运行。
2021-03-19 02:46:24
我一直在研究 PapaParse 如何处理 Web Workers,他们似乎采用了这种方法github.com/mholt/PapaParse
2021-03-25 02:46:24
这是一个有趣的方法。FWIW,我通过检查“self”(Web Worker 全局对象)与“window”的存在来检测 Web Worker。
2021-03-30 02:46:24
我不明白来自脚本元素的 previousSibling 是什么。有人可以解释我吗?
2021-04-06 02:46:24
您的网站似乎已消失;你有新的网址吗?
2021-04-08 02:46:24

使用该Blob方法,对于工人工厂来说如何:

var BuildWorker = function(foo){
   var str = foo.toString()
             .match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
   return  new Worker(window.URL.createObjectURL(
                      new Blob([str],{type:'text/javascript'})));
}

所以你可以像这样使用它......

var myWorker = BuildWorker(function(){
   //first line of worker
   self.onmessage(){....};
   //last line of worker
});

编辑:

我刚刚进一步扩展了这个想法,以便更轻松地进行跨线程通信:bridged-worker.js

编辑2:

上面的链接是我创建的一个要点。后来有人把它变成了一个真正的 repo

你能多说一下那个正则表达式的作用以及为什么吗?既然它有“功能”,我猜它不会在箭头函数上工作?还有什么限制吗?
2021-03-13 02:46:24

Web 工作者在完全独立的上下文中作为单个程序运行。

这意味着代码不能以对象形式从一个上下文移动到另一个上下文,因为它们可以通过属于另一个上下文的闭包来引用对象。
这一点尤其重要,因为 ECMAScript 被设计为单线程语言,并且由于 Web Worker 在单独的线程中运行,因此您将面临执行非线程安全操作的风险。

这再次意味着需要使用源代码形式的代码初始化网络工作者。

WHATWG的规范

如果生成的绝对 URL 的来源与入口脚本的来源不同,则抛出 SECURITY_ERR 异常。

因此,脚本必须是与原始页面具有相同方案的外部文件:您无法从 data: URL 或 javascript: URL 加载脚本,并且 https: 页面无法使用带有 http: URL 的脚本启动工作程序。

但不幸的是,它并没有真正解释为什么不能允许将带有源代码的字符串传递给构造函数。