Javascript 如何从字符串创建 Web Worker

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10343913/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:57:47  来源:igfitidea点击:

How to create a Web Worker from a string

javascriptweb-workerdata-uri

提问by bigblind

How can I use create a Web worker from a string (which is supplied via a POST request)?

如何从字符串(通过 POST 请求提供)创建 Web 工作者?

One way I can think of, but I'm not sure how to implement it, is by creating a data-URI from the server response, and passing that to the Worker constructor, but I've heard that some browsers don't allow this, because of the same origin policy.

我能想到的一种方法,但我不确定如何实现它,是从服务器响应创建一个数据 URI,并将其传递给 Worker 构造函数,但我听说有些浏览器不允许这是因为同源策略。

MDN states the uncertainty about the origin policy around data URI's:

MDN 说明了围绕数据 URI 的原始策略的不确定性

Note: The URI passed as parameter of the Worker constructor must obey the same-origin policy. There is currently disagreement among browsers vendors on whether data URIs are of the same-origin or not; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) and later do allow data URIs as a valid script for workers. Other browsers may disagree.

注意:作为 Worker 构造函数的参数传递的 URI 必须遵守同源策略。目前浏览器厂商对数据URI是否同源存在分歧;Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) 及更高版本确实允许数据 URI 作为工作人员的有效脚本。其他浏览器可能不同意。

Here's also a post discussing it on the whatwg.

这里还有一篇在 whatwg 上讨论它的帖子

回答by Rob W

Summary

  • blob:for Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+
  • data:application/javascriptfor Opera 10.60 - 12
  • evalotherwise (IE 10+)

概括

  • blob:适用于 Chrome 8+、Firefox 6+、Safari 6.0+、Opera 15+
  • data:application/javascript适用于 Opera 10.60 - 12
  • eval否则(IE 10+)

URL.createObjectURL(<Blob blob>)can be used to create a Web worker from a string. The blob can be created using the BlobBuilderAPI deprecatedor the Blobconstructor.

URL.createObjectURL(<Blob blob>)可用于从字符串创建 Web Worker。可以使用已弃用BlobBuilderAPI或构造函数来创建 blob 。Blob

Demo: http://jsfiddle.net/uqcFM/49/

演示: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');

Compatibility

兼容性

Web workers are supported in the following browsers source:

以下浏览器支持 Web Worker :

  • Chrome 3
  • Firefox 3.5
  • IE 10
  • Opera 10.60
  • Safari 4
  • 铬 3
  • 火狐 3.5
  • 浏览器 10
  • 歌剧 10.60
  • 野生动物园 4

This method's support is based on the support of the BlobAPI and the URL.createObjectUrlmethod. Blobcompatibility:

这个方法的支持是基于BlobAPI和方法的支持URL.createObjectUrlBlob兼容性

  • Chrome 8+ (WebKitBlobBuilder), 20+ (Blobconstructor)
  • Firefox 6+ (MozBlobBuilder), 13+ (Blobconstructor)
  • Safari 6+ (Blobconstructor)
  • Chrome 8+ ( WebKitBlobBuilder), 20+ (Blob构造函数)
  • Firefox 6+ ( MozBlobBuilder), 13+ (Blob构造函数)
  • Safari 6+(Blob构造函数)

IE10 supports MSBlobBuilderand URL.createObjectURL. However, trying to create a Web Worker from a blob:-URL throws a SecurityError.

IE10 支持MSBlobBuilderURL.createObjectURL. 但是,尝试从blob:-URL创建 Web Worker会引发 SecurityError。

Opera 12 does not support URLAPI. Some users may have a fake version of the URLobject, thanks to this hack in browser.js.

Opera 12 不支持URLAPI。有些用户可能有仿版URL的对象,这要归功于这个技巧在browser.js

Fallback 1: data-URI

回退 1:数据 URI

Opera supports data-URIs as an argument to the Workerconstructor. Note: Do not forget to escape special characters(Such as #and %).

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

Demo: http://jsfiddle.net/uqcFM/37/

演示:http: //jsfiddle.net/uqcFM/37/

Fallback 2: Eval

回退 2:评估

evalcan be used as a fallback for Safari (<6) and IE 10.

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

回答by Chanakya Vadla

I agree with the current accepted answer but often editing and managing the worker code will be hectic as its in the form of a string.

我同意当前接受的答案,但通常编辑和管理工作人员代码会很忙,因为它以字符串的形式出现。

So optionally we can use the below approach where we can keep the worker as a function, and then covert to string->blob:

所以我们可以选择使用下面的方法,我们可以将 worker 保持为一个函数,然后转换为 string->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.

This is tested in IE11+ and FF and Chrome

这在 IE11+ 和 FF 以及 Chrome 中进行了测试

回答by lukelalo

I've made an approach with most of your ideas and adding some of mine. The only thing my code needs on worker is to use 'this' to refer 'self' scope. I'm pretty sure that this is very improvable:

我已经采用了你的大部分想法并添加了我的一些想法。我的代码对 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');
}

Demo: http://jsfiddle.net/8N6aR/

演示:http: //jsfiddle.net/8N6aR/

回答by Chris GW Green

Nice answer - I've been working on a similar problem today when trying to create Web Workers with fallback capabilities when they're not available (i.e. run worker script in main thread). As this thread is pertains to the topic, I thought I'd provide my solution here:

不错的答案 - 今天我一直在解决类似的问题,当它们不可用时(即在主线程中运行工作脚本),我尝试创建具有回退功能的 Web 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 [email protected] 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>

回答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

一个例子是

// 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
});

回答by Chad Scira

Expanding on @Chanu_Sukarno's code, you can simply pass in a worker function (or string) to this function and it will execute it inside a web worker:

扩展@Chanu_Sukarno 的代码,你可以简单地将一个工作函数(或字符串)传递给这个函数,它会在一个 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);
  });
}

Here's an example of how to use it:

以下是如何使用它的示例:

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")




In nodejs, doWorkerTasklooks like this:

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);
  });
}

回答by trusktr

The accepted answer is a bit complex, due to supporting backwards compatibility, so I wanted to post the same thing but simplified. Try this in your (modern) browser console:

由于支持向后兼容性,接受的答案有点复杂,所以我想发布相同但简化的内容。在您的(现代)浏览器控制台中试试这个:

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.

回答by trusktr

You can get real-data from the objectURLand not just blob by changing the responseTypeto either "text"or "arraybuffer".

通过将 更改为或 ,您可以从objectURL获取真实数据,而不仅仅是 blob 。responseType"text""arraybuffer"

Here is a back-and-forth conversionof text/javascriptto blobto objectURLback to blobor text/javascript.

这是从to到back to or的来回转换text/javascriptblobobjectURLblobtext/javascript

if you are wondering, I'm using it to generate a web-worker with no external files
you may use it to return binary content, for example a YouTube video ;) (from the <video> tag resource attribute)

如果您想知道,我正在使用它来生成一个没有外部文件网络工作者,
您可以使用它来返回二进制内容,例如 YouTube 视频;)(来自 <video> 标签资源属性)

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)}'
*/

回答by zevero

Use my tiny plugin https://github.com/zevero/worker-create

使用我的小插件https://github.com/zevero/worker-create

var worker_url = Worker.create("self.postMessage('Example post from Worker');");
var worker = new Worker(worker_url);

But you may also give it a function.

但是你也可以给它一个函数。