没有单独的 Javascript 文件的 Web 工作者?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5408406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Web workers without a separate Javascript file?
提问by Ben Dilts
As far as I can tell, web workers need to be written in a separate JavaScript file, and called like this:
据我所知,网络工作者需要写在一个单独的 JavaScript 文件中,并像这样调用:
new Worker('longrunning.js')
I'm using the closure compiler to combine and minify all my JavaScript source code, and I'd rather not have to have my workers in separate files for distribution. Is there some way to do this?
我正在使用闭包编译器来组合和缩小我所有的 JavaScript 源代码,而且我不想让我的工作人员在单独的文件中进行分发。有没有办法做到这一点?
new Worker(function() {
//Long-running work here
});
Given that first-class functions are so crucial to JavaScript, why does the standard way to do background work have to load a whole other JavaScript file from the web server?
鉴于一流的函数对 JavaScript 如此重要,为什么执行后台工作的标准方法必须从 Web 服务器加载整个其他 JavaScript 文件?
采纳答案by vsync
http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers
http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers
What if you want to create your worker script on the fly, or create a self-contained page without having to create separate worker files? With Blob(), you can "inline" your worker in the same HTML file as your main logic by creating a URL handle to the worker code as a string
如果您想即时创建您的工作程序脚本,或者创建一个独立的页面而不必创建单独的工作程序文件,该怎么办?使用 Blob(),您可以通过将工作程序代码的 URL 句柄创建为字符串,在与主要逻辑相同的 HTML 文件中“内联”您的工作程序
Full example of BLOB inline worker:
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>
回答by Adria
The html5rocks solution of embedding the web worker code in HTML is fairly horrible.
And a blob of escaped JavaScript-as-a-string is no better, not least because it complicates work-flow (Closure compiler can't operate on strings).
在 HTML 中嵌入 web worker 代码的 html5rocks 解决方案相当糟糕。
一团转义的 JavaScript-as-a-string 也好不到哪里去,尤其是因为它使工作流程复杂化(闭包编译器不能对字符串进行操作)。
Personally I really like the toString methods, but @dan-manTHAT regex!
我个人非常喜欢 toString 方法,但是@dan-man那个正则表达式!
My preferred approach:
我的首选方法:
// 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 is the intersection of these three tables:
支持是这三个表的交集:
- http://caniuse.com/#feat=webworkers
- http://caniuse.com/#feat=blobbuilder
- http://caniuse.com/#feat=bloburls
- http://caniuse.com/#feat=webworkers
- http://caniuse.com/#feat=blobbuilder
- http://caniuse.com/#feat=bloburls
This won't work for a SharedWorkerhowever, because the URL must be an exact match, even if the optional 'name' parameter matches. For a SharedWorker, you'll need a separate JavaScript file.
但是,这不适用于SharedWorker,因为 URL 必须完全匹配,即使可选的“name”参数匹配。对于 SharedWorker,您需要一个单独的 JavaScript 文件。
2015 update - The ServiceWorker singularity arrives
2015 年更新 - ServiceWorker 奇点到来
Now there's an even more powerful way of solving this problem. Again, store the worker code as a function, (rather than a static string) and convert using .toString(), then insert the code into CacheStorage under a static URL of your choice.
现在有一种更强大的方法可以解决这个问题。同样,将工作代码存储为函数(而不是静态字符串)并使用 .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'}})
);
});
There are two possible fall-backs. ObjectURL as above, or more seamlessly, put a realJavaScript file at /my_workers/worker1.js
有两种可能的回退。ObjectURL 如上所述,或者更无缝地,将一个真正的JavaScript 文件放在 /my_workers/worker1.js
Advantages of this approach are:
这种方法的优点是:
- SharedWorkers can also be supported.
- Tabs can share a single cached copy at a fixed address. The blob approach proliferates random objectURLs for every tab.
- 也可以支持 SharedWorkers。
- 选项卡可以在固定地址共享单个缓存副本。blob 方法为每个选项卡增加随机的 objectURL。
回答by Delan Azabani
You can create a single JavaScript file that is aware of its execution context and can act as both a parent script and a worker. Let's start off with a basic structure for a file like this:
您可以创建单个 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);
As you can see, the script contains all code for both the parent's and the worker's point of view, checking if its own individual instance is a worker with !document
. The somewhat unwieldy script_path
computation is used to accurately calculate the script's path relative to the parent page, as the path supplied to new Worker
is relative to the parent page, not the script.
如您所见,该脚本包含父级和工作人员观点的所有代码,检查其自己的单个实例是否是带有!document
. 有点笨拙的script_path
计算用于准确计算脚本相对于父页面的路径,因为提供给的路径new Worker
是相对于父页面而不是脚本的。
回答by dan-man
Using the Blob
method, how about this for a worker factory:
使用该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'})));
}
So you could use it like this...
所以你可以像这样使用它......
var myWorker = BuildWorker(function(){
//first line of worker
self.onmessage(){....};
//last line of worker
});
EDIT:
编辑:
I've just extended this idea further to make it easier to do cross-thread communication: bridged-worker.js.
我刚刚进一步扩展了这个想法,以便更轻松地进行跨线程通信:bridged-worker.js。
EDIT 2:
编辑2:
The above link is to a gist I created. Someone else later turned it into an actual repo.
上面的链接是我创建的一个要点。后来有人把它变成了一个真正的 repo。
回答by Sean Kinsey
Web workers operate in entirely separate contexts as individual Program's.
Web 工作者在完全独立的上下文中作为单个程序运行。
This means that code cannot be moved from one context to another in object form, as they would then be able to reference objects via closures belonging to the other context.
This is especially crucial as ECMAScript is designed to be a single threaded language, and since web workers operate in separate threads, you would then have the risk of non-thread-safe operations being performed.
这意味着代码不能以对象形式从一个上下文移动到另一个上下文,因为它们可以通过属于另一个上下文的闭包来引用对象。
这一点尤其重要,因为 ECMAScript 被设计为一种单线程语言,并且由于 Web Worker 在单独的线程中运行,因此您将面临执行非线程安全操作的风险。
This again means that web workers need to be initialized with code in source form.
这再次意味着需要使用源代码形式的代码初始化网络工作者。
The spec from WHATWGsays
WHATWG的规范说
If the origin of the resulting absolute URL is not the same as the origin of the entry script, then throw a SECURITY_ERR exception.
Thus, scripts must be external files with the same scheme as the original page: you can't load a script from a data: URL or javascript: URL, and an https: page couldn't start workers using scripts with http: URLs.
如果生成的绝对 URL 的来源与入口脚本的来源不同,则抛出 SECURITY_ERR 异常。
因此,脚本必须是与原始页面具有相同方案的外部文件:您无法从 data: URL 或 javascript: URL 加载脚本,并且 https: 页面无法使用带有 http: URL 的脚本启动工作程序。
but unfortunately it doesn't really explain why one couldn't have allowed passing a string with source code to the constructor.
但不幸的是,它并没有真正解释为什么不允许将带有源代码的字符串传递给构造函数。
回答by Chris Tobba
a better to read way for a inline worker..
内联工作人员的更好阅读方式..
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");
回答by ubershmekel
Taking Adria's response and putting it in a copy-pastable function which works with current Chrome and FF but not IE10 (worker from blob causes a security error).
获取 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;
}
And here's a working example http://jsfiddle.net/ubershmekel/YYzvr/
回答by GG.
Recent answer (2018)
最近的回答(2018)
You can use Greenlet:
您可以使用Greenlet:
Move an async function into its own thread. A simplified single-function version of Workerize.
将异步函数移动到它自己的线程中。Workerize 的简化单功能版本。
Example:
例子:
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'))
回答by Chad Scira
Depending on your use case you can use something like
根据您的用例,您可以使用类似的东西
task.jsSimplified interface for getting CPU intensive code to run on all cores (node.js, and web)
task.js用于让 CPU 密集型代码在所有内核(node.js 和 web)上运行的简化接口
A example would be
一个例子是
function blocking (exampleArgument) {
// block thread
}
// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);
// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
// do something with result
});
回答by vadimk
Take a look at the vkThread plugin. With htis plugin you can take any function in your main code and execute it in a thread (web worker). So, you don't need to create a special "web-worker file".
看看 vkThread 插件。使用 htis 插件,您可以在主代码中使用任何函数并在线程(网络工作者)中执行它。因此,您无需创建特殊的“网络工作者文件”。
http://www.eslinstructor.net/vkthread/
http://www.eslinstructor.net/vkthread/
--Vadim
--瓦迪姆