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

Ben*_*lts 279 javascript web-worker

据我所知,Web worker需要在一个单独的JavaScript文件中编写,并像这样调用:

new Worker('longrunning.js')
Run Code Online (Sandbox Code Playgroud)

我正在使用闭包编译器来组合和缩小我的所有JavaScript源代码,而我宁愿不必将我的worker放在单独的文件中进行分发.有办法做到这一点吗?

new Worker(function() {
    //Long-running work here
});
Run Code Online (Sandbox Code Playgroud)

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

vsy*_*ync 208

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>
Run Code Online (Sandbox Code Playgroud)

  • IE10中可能支持Blob构造函数,但您仍然无法通过它将javascript传递给Web worker(甚至在IE11中也没有):https://connect.microsoft.com/IE/feedback/details/801810/web-workers-from -blob的URL-在-IE-10和-11. (3认同)
  • BlobBuiler [现已弃用](https://developer.mozilla.org/docs/DOM/BlobBuilder).请改用[Blob](https://developer.mozilla.org/docs/DOM/Blob).目前最新的Firefox/WebKit/Opera和IE10支持,请参阅旧版浏览器的[兼容性表](http://caniuse.com/#search=blob). (2认同)
  • @albanx-您是否愿意至少说出您使用哪个深奥的浏览器挂起?这个演示适合您吗?http://ie.microsoft.com/testdrive/Graphics/WorkerFountains/Default.html (2认同)

Adr*_*ria 150

将HTML工作者代码嵌入HTML的html5rocks解决方案相当糟糕.
并且一堆逃脱的JavaScript-as-a-string并不是更好,尤其是因为它使工作流变得复杂(Closure编译器无法对字符串进行操作).

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

我的首选方法:

// 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 );
Run Code Online (Sandbox Code Playgroud)

支持是这三个表的交集:

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


2015年更新 - ServiceWorker奇点到来

现在有一个更强大的方法来解决这个问题.再次,将worker代码存储为函数(而不是静态字符串)并使用.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'}})
 );
});
Run Code Online (Sandbox Code Playgroud)

有两种可能的后退.的ObjectURL如上,或更无缝地,把一个真正的 JavaScript文件在/my_workers/worker1.js

这种方法的优点是:

  1. 也可以支持SharedWorkers.
  2. 选项卡可以在固定地址共享单个缓存副本.blob方法为每个选项卡增加随机objectURL.

  • 这个解决方案的浏览器兼容性会是什么样的? (4认同)
  • 我喜欢这个答案(第一个),但它不允许任何导入或“importorScripts”语句。我们如何才能将此方法与将其他脚本或模块导入*到*工作器中结合起来? (2认同)

Del*_*ani 36

您可以创建一个知道其执行上下文的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);
Run Code Online (Sandbox Code Playgroud)

如您所见,该脚本包含父级和工作者角度的所有代码,检查其自己的个体实例是否为工作者!document.稍微笨拙的script_path计算用于准确计算脚本相对于父页面的路径,因为提供的路径new Worker是相对于父页面而不是脚本.

  • 您的网站似乎已经消失; 你有新的URL吗? (4认同)
  • 优雅的方法! (2认同)

dan*_*man 27

使用该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'})));
}
Run Code Online (Sandbox Code Playgroud)

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

var myWorker = BuildWorker(function(){
   //first line of worker
   self.onmessage(){....};
   //last line of worker
});
Run Code Online (Sandbox Code Playgroud)

编辑:

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

编辑2:

以上链接是我创建的要点.其他人后来把它变成了一个真正的回购.


Sea*_*sey 11

Web工作者在完全独立的环境中作为单独的程序运行.

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

这再次意味着需要使用源代码中的代码初始化Web worker.

WHATWG的规范说

如果生成的绝对URL的原点与条目脚本的原点不同,则抛出SECURITY_ERR异常.

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

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


tri*_*cot 7

一个简单的承诺版本 ,Function#callAsWorker它接受 thisArg 和参数(就像call),并返回一个承诺:

Function.prototype.callAsWorker = function (...args) {
    return new Promise( (resolve, reject) => {
        const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
            blob = new Blob([code], { type: "text/javascript" }),
            worker = new Worker(window.URL.createObjectURL(blob));
        worker.onmessage = e => (resolve(e.data), worker.terminate());
        worker.onerror = e => (reject(e.message), worker.terminate());
        worker.postMessage(args);
    });
}

// Demo
function add(...nums) {
    return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
    console.log('result: ', result);
});
Run Code Online (Sandbox Code Playgroud)


Chr*_*bba 6

内联工作者更好的阅读方式..

    var worker_fn = function(e) 
    {
        self.postMessage('msg from worker');            
    };

    var blob = new Blob(["onmessage ="+worker_fn.toString()], { type: "text/javascript" });

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) 
    {
       alert(e.data);
    };
    worker.postMessage("start"); 
Run Code Online (Sandbox Code Playgroud)


ube*_*kel 5

采取Adria的响应并将其置于可复制复制的函数中,该函数可与当前的Chrome和FF一起使用,但不适用于IE10(来自blob的工作人员会导致安全错误)。

var newWorker = function (funcObj) {
    // Build a worker from an anonymous function body
    var blobURL = URL.createObjectURL(new Blob(
        ['(', funcObj.toString(), ')()'],
        {type: 'application/javascript'}
     ));

    var worker = new Worker(blobURL);

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

    return worker;
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作示例http://jsfiddle.net/ubershmekel/YYzvr/


GG.*_*GG. 5

最近回答(2018)

您可以使用Greenlet

将异步函数移到其自己的线程中。Workerize的简化的单功能版本。

例:

import greenlet from 'greenlet'

const getName = greenlet(async username => {
  const url = `https://api.github.com/users/${username}`
  const res = await fetch(url)
  const profile = await res.json()
  return profile.name
})

console.log(await getName('developit'))
Run Code Online (Sandbox Code Playgroud)