如何从字符串创建 Web 工作线程
How to create a Web Worker from a string
如何使用从字符串(通过 POST 请求提供)创建 Web worker ?
我能想到的一种方法,但我不确定如何实现它,是从服务器响应创建一个 data-URI,并将其传递给 Worker 构造函数,但我听说有些浏览器不允许这样做,因为同源策略。
MDN 指出了围绕数据 URI 的源策略的不确定性:
注意:作为 Worker 构造函数的参数传递的 URI 必须遵循同源策略。目前,浏览器供应商对数据 URI 是否具有同源存在分歧;Gecko 10.0(Firefox 10.0/Thunderbird 10.0)及更高版本允许数据URI作为工作线程的有效脚本。其他浏览器可能不同意。
这里也有一篇在whatwg上讨论它的帖子。
总结
blob:
适用于 Chrome 8+、Firefox 6+、Safari 6.0+、Opera 15+- 歌剧
data:application/javascript
10.60 - 12eval
否则 (IE 10+)
URL.createObjectURL(<Blob blob>)
可用于从字符串创建 Web 辅助角色。可以使用已弃用的 BlobBuilder
API 或Blob
构造函数创建 Blob。
演示:http://jsfiddle.net/uqcFM/49/
// URL.createObjectURL
window.URL = window.URL || window.webkitURL;
// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";
var blob;
try {
blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(response);
blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));
// Test, used in all examples:
worker.onmessage = function(e) {
alert('Response: ' + e.data);
};
worker.postMessage('Test');
兼容性
以下浏览器源支持 Web 辅助角色:
- 铬 3
- 火狐 3.5
- IE 10
- 歌剧 10.60
- 野生动物园 4
此方法的支持基于 Blob
API 和 URL.createObjectUrl
方法的支持。 Blob
兼容性:
- 铬 8+ (
WebKitBlobBuilder
), 20+ (Blob
构造函数) - 火狐 6+ (
MozBlobBuilder
), 13+ (Blob
构造函数) - Safari 6+ (
Blob
构造函数)
IE10支持MSBlobBuilder
和URL.createObjectURL
。但是,尝试从 blob:
-URL 创建 Web Worker 会引发安全错误。
Opera 12 不支持URL
API。一些用户可能拥有URL
对象的虚假版本,这要归功于browser.js
中的这个黑客。
回退 1:数据 URI
Opera 支持数据 URI 作为Worker
构造函数的参数。注意:不要忘记转义特殊字符(如#
和%
)。
// response as defined in the first example
var worker = new Worker('data:application/javascript,' +
encodeURIComponent(response) );
// ... Test as defined in the first example
演示:http://jsfiddle.net/uqcFM/37/
回退 2:评估
eval
可以用作 Safari (<6) 和 IE 10 的后备。
// Worker-helper.js
self.onmessage = function(e) {
self.onmessage = null; // Clean-up
eval(e.data);
};
// Usage:
var worker = new Worker('Worker-helper.js');
// `response` as defined in the first example
worker.postMessage(response);
// .. Test as defined in the first example
我同意当前接受的答案,但通常编辑和管理工人代码会很忙,因为它是字符串的形式。
因此,我们可以选择使用以下方法,我们可以将worker保留为一个函数,然后隐蔽到字符串>blob:
// function to be your worker
function workerFunction() {
var self = this;
self.onmessage = function(e) {
console.log('Received input: ', e.data); // message received from main thread
self.postMessage("Response back to main thread");
}
}
///////////////////////////////
var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds "use strict"; to any function which might block worker execution so knock it off
var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
type: 'application/javascript; charset=utf-8'
});
var worker = new Worker(blobURL); // spawn new worker
worker.onmessage = function(e) {
console.log('Worker said: ', e.data); // message received from worker
};
worker.postMessage("some input to worker"); // Send data to our worker.
这在IE11+和FF和Chrome中进行了测试
由于支持向后兼容性,接受的答案有点复杂,所以我想发布同样的东西,但进行了简化。在您的(现代)浏览器控制台中尝试此操作:
const code = "console.log('Hello from web worker!')"
const blob = new Blob([code], {type: 'application/javascript'})
const worker = new Worker(URL.createObjectURL(blob))
// See the output in your console.
我已经用你的大部分想法做了一种方法,并添加了我的一些想法。我的代码在 worker 上唯一需要的就是使用"this"来引用"self"范围。我很确定这是可以改进的:
// Sample code
var code = function() {
this.onmessage = function(e) {
this.postMessage('Worker: '+e.data);
this.postMessage('Worker2: '+e.data);
};
};
// New thread worker code
FakeWorkerCode = function(code, worker) {
code.call(this);
this.worker = worker;
}
FakeWorkerCode.prototype.postMessage = function(e) {
this.worker.onmessage({data: e});
}
// Main thread worker side
FakeWorker = function(code) {
this.code = new FakeWorkerCode(code, this);
}
FakeWorker.prototype.postMessage = function(e) {
this.code.onmessage({data: e});
}
// Utilities for generating workers
Utils = {
stringifyFunction: function(func) {
// Stringify the code
return '(' + func + ').call(self);';
},
generateWorker: function(code) {
// URL.createObjectURL
windowURL = window.URL || window.webkitURL;
var blob, worker;
var stringified = Utils.stringifyFunction(code);
try {
blob = new Blob([stringified], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(stringified);
blob = blob.getBlob();
}
if ("Worker" in window) {
worker = new Worker(windowURL.createObjectURL(blob));
} else {
worker = new FakeWorker(code);
}
return worker;
}
};
// Generate worker
var worker = Utils.generateWorker(code);
// Test, used in all examples:
worker.onmessage = function(e) {
alert('Response: ' + e.data);
};
function runWorker() {
worker.postMessage('working fine');
}
演示:http://jsfiddle.net/8N6aR/
不错的答案 - 我今天一直在研究类似的问题,当它们不可用时尝试创建具有回退功能的 Web Workers(即在主线程中运行 worker 脚本)。由于此线程与该主题有关,因此我想我会在这里提供我的解决方案:
<script type="javascript/worker">
//WORKER FUNCTIONS
self.onmessage = function(event) {
postMessage('Hello, ' + event.data.name + '!');
}
</script>
<script type="text/javascript">
function inlineWorker(parts, params, callback) {
var URL = (window.URL || window.webkitURL);
if (!URL && window.Worker) {
var worker = new window.Worker(URL.createObjectURL(new Blob([parts], { "type" : "text/javascript" })));
worker.onmessage = function(event) {
callback(event.data);
};
worker.postMessage(params);
} else {
var postMessage = function(result) {
callback(result);
};
var self = {}; //'self' in scope of inlineWorker.
eval(parts); //Converts self.onmessage function string to function on self via nearest scope (previous line) - please email chrisgwgreen.site@gmail.com if this could be tidier.
self.onmessage({
data: params
});
}
}
inlineWorker(
document.querySelector('[type="javascript/worker"]').textContent,
{
name: 'Chaps!!'
},
function(result) {
document.body.innerHTML = result;
}
);
</script>
</body>
扩展@Chanu_Sukarno的代码,你可以简单地将一个worker函数(或字符串)传递给这个函数,它将在Web worker中执行它:
async function doWorkerTask(workerFunction, input, buffers) {
// Create worker
let fnString = '(' + workerFunction.toString().replace('"use strict";', '') + ')();';
let workerBlob = new Blob([fnString]);
let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
let worker = new Worker(workerBlobURL);
// Run worker
return await new Promise(function(resolve, reject) {
worker.onmessage = function(e) { resolve(e.data); };
worker.postMessage(input, buffers);
});
}
下面是如何使用它的示例:
function myTask() {
self.onmessage = function(e) {
// do stuff with `e.data`, then:
self.postMessage("my response");
self.close();
}
}
let output = await doWorkerTask(myTask, input, inputBuffers);
// now you can do something with `output` (which will be equal to "my response")
在nodejs中,doWorkerTask
看起来像这样:
async function doWorkerTask(workerFunction, input, buffers) {
let Worker = require('webworker-threads').Worker;
let worker = new Worker(workerFunction);
// Run worker
return await new Promise(function(resolve, reject) {
worker.onmessage = function(e) { resolve(e.data); };
worker.postMessage(input, buffers);
});
}
这是一种替代方法,可让您使用具有可选设置参数的函数定义工作线程。
注意 - 您只能使用可以字符串化的设置参数 - 使用 worker.postMessage 发送任何其他内容
// define our worker function
function workerFunc(arg1,arg2) {
console.log("worker is running:"+arg1+" "+arg2);
postMessage(arg1+" "+arg2);
setInterval(sendTime,1000);
function sendTime(){
postMessage(new Date().toUTCString() );
}
}
// (optionally) define an element to inspect the script that worker will use
workerFunc.debug = document.getElementById("worker_script");
// start the worker and get replies from it
startWorker(workerFunc,"hello","world").addEventListener("message",function(e){
document.getElementById("worker_reply").innerHTML = e.data;
});
function startWorker (fn) {
const src = fn.toString();
const args = src.substring(src.indexOf("(")+1,src.indexOf(")"));
const code = ( args ? "let ["+args+"]="+JSON.stringify([].slice.call(arguments,1))+";'n" : "" )+ src.substring(src.indexOf("{")+1,src.length-1);
const blob = new Blob([code], {type: 'application/javascript'})
if (fn.debug) fn.debug.innerHTML=code;
return new Worker(URL.createObjectURL(blob))
}
<textarea id="worker_reply"></textarea>
<h1>Worker Script</h1>
<pre id="worker_script">
</pre>
使用我的小插件 https://github.com/zevero/worker-create
var worker_url = Worker.create("self.postMessage('Example post from Worker');");
var worker = new Worker(worker_url);
但你也可以给它一个功能。
根据您的用例,您可以使用类似的东西
task.js 简化的界面,用于在所有内核(节点.js和 Web)上运行 CPU 密集型代码
一个例子是
// turn blocking pure function into a worker task
const functionFromPostRequest = task.wrap('function (exampleArgument) {}');
// run task on a autoscaling worker pool
functionFromPostRequest('exampleArgumentValue').then(result => {
// do something with result
});
我的解决方案(可以很容易地"承诺"...
function makeWorker(workerFunction, cb) {
// Create worker
var tplFun = "onmessage = function(e){console.log(e); var args = Array.prototype.slice.call(e.data); var res=___.apply(this,args);postMessage(res);}"
var fnTxt = workerFunction.toString().replace('"use strict";', '');
var final = tplFun.replace("___", fnTxt);
let workerBlob = new Blob([final]);
let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
let worker = new Worker(workerBlobURL);
return function () {
var args = Array.prototype.slice.call(arguments);
console.log(args)
worker.postMessage(args);
worker.onmessage = function (e) {
console.log(e.data);
cb(e.data);
}
}
}
function myTask(a, b, c) {
return a * b * c;
}
function onresult(e) {
alert(e);
}
var fn = makeWorker(myTask, onresult)
fn(1, 2, 3);
可以很好地使用"纯而慢"功能!
基于接受的答案,类到工人,有趣的话题
// worker class
class SimpleWorker {
constructor() {
console.log("simple worker init");
}
onMessage(event) {
console.log("main to worker", event.data);
postMessage({
type: "answerswer",
data: "data from worker"
});
}
}
// class to worker
const workerFromClass = workerClassRef => {
console.log(workerClassRef.name, "to worker");
// factory method, converted to string, used to instanciate worker
let workerFactory = (self, workerClass) => {
let worker = new workerClass();
self["onmessage"] = worker.onMessage.bind(worker);
};
// compute worker code string
// worker class & factory function
let str = workerClassRef.toString() + "'n"
+ "(" + workerFactory.toString() + ")"
+ "(this," + workerClassRef.name + ");"
// worker code to blob
let blob = new Blob(
[str],
{type: "application/javascript"}
);
// return worker instance
return new Worker(
URL.createObjectURL(blob)
);
};
// main
// create worker
let worker = workerFromClass(SimpleWorker);
// handle messages from worker
worker.addEventListener(
"message",
event => console.log("worker to main", event.data)
);
// send message to worker
let message = {
type: "question",
data: "data from main"
};
console.log("main to worker", message);
worker.postMessage(message);
responseType
更改为 "text"
或 "arraybuffer"
来从对象 URL 获取真实数据,而不仅仅是 blob
这是text/javascript
到blob
objectURL
blob
或text/javascript
的来回转换。
如果您想知道,我正在使用它来生成一个没有外部文件的
Web worker您可以使用它返回二进制内容,例如 YouTube 视频;)(来自 <视频> 标记资源属性)视频>
var blob = new Blob(['self.onmessage=function(e){postMessage(e)}'],{type: 'text/javascript'}); //->console: (object) Blob {size: 42, type: "text/javascript", slice: function}
var obju = URL.createObjectURL(js_blob); //->console: "blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7"
var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7', true);
xhr.responseType = 'text'; /* or "blob" */
xhr.onreadystatechange = function(){
if(xhr.DONE !== xhr.readyState) return;
console.log(xhr.response);
}
xhr.send();
/*
responseType "blob" ->console: (object) Blob {size: 42, type: "text/javascript", slice: function}
responseType "text" ->console: (text) 'self.onmessage=function(e){postMessage(e)}'
*/
- importScripts在web工作程序中返回未定义的
- 使用web工作程序进行多个并发繁重计算
- Chrome没有加载最新版本的web工作程序脚本(运行缓存版本)
- web工作程序中不同的服务器请求行为
- Web 工作线程中的同步 XHR 请求是否仍会锁定浏览器
- Web工作程序在处理大型数组时内存不足
- 如何将数据(如用户 ID)传递给 Web 工作人员,以便从服务器获取其他推送通知数据
- Web 工作线程/画布的内存泄漏
- 关于 Web 工作线程并行性
- 如何从字符串创建 Web 工作线程
- 处理 AJAX 调用的 Web 工作人员 - 优化矫枉过正
- js web工作程序内存泄漏
- 在执行不区分大小写的排序以删除web工作程序中的重复项时,首选大写字母唯一
- Web工作程序在传递具有数组缓冲区的大型对象数组时会崩溃Chrome选项卡
- 我可以在浏览器ui线程和web工作线程中安全地使用html5中的哪些本地存储
- 从web工作程序和主浏览器线程访问相同的websql数据库
- 没有web工作程序的Javascript后台处理
- 当标签关闭时,HTML5 web工作线程会发生什么's正在跑步
- 如何在Web工作线程中使用javascript变量
- 如何使用jQuery从Web工作线程中解析XML