如何从字符串创建Web Worker


83

如何使用通过字符串(通过POST请求提供)创建Web Worker?

我可以想到的一种方法(但我不确定如何实现)是通过从服务器响应创建数据URI,并将其传递给Worker构造函数,但是我听说有些浏览器不允许这是因为相同的原产地政策。

MDN指出围绕数据URI的起源策略的不确定性

注意:作为Worker构造函数的参数传递的URI必须遵守同源策略。当前,浏览器供应商之间在数据URI是否具有相同来源方面存在分歧。Gecko 10.0(Firefox 10.0 / Thunderbird 10.0)及更高版本的确允许数据URI作为工作程序的有效脚本。其他浏览器可能会不同意。

这也是在whatwg上讨论它的帖子


我想知道CORS(w3.org/TR/cors)是否会有所帮助。HTMl5rocks在针对工人的相同来源政策(html5rocks.com/en/tutorials/workers/basics)时使用了强大的“必须”语言。因此,也许CORS在这里没有太大帮助。你有尝试过吗?
Pavel Veller

Answers:


144

概要

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

URL.createObjectURL(<Blob blob>)可用于从字符串创建Web Worker。可以使用已弃用BlobBuilderAPI或构造函数创建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 Worker :

  • 铬3
  • Firefox 3.5
  • IE 10
  • 歌剧10.60
  • Safari 4

该方法的支持基于BlobAPI和方法的支持URL.createObjectUrlBlob相容性

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

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

Opera 12不支持URLAPI。URL由于中的hack,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

3
@BrianFreid感谢您的编辑,但这不是必需的。如果再看几行,将会看到“ IE10支持MSBlobBuilderURL.createObjectURL。但是,尝试从blob:-URL创建Web Worker会引发SecurityError。”。因此,添加MSBlobBuilder将无效,唯一的选择是后备#2。
罗伯W

Opera 12不再定义URL(因此也没有定义任何属性),并且现在已经足够支持Blob构造函数。
gsnedders 2013年

2
我已验证至少在预览版中IE11仍会发生这种情况。
本杰明·格林巴姆2013年

1
是否仅在Opera或所有其他浏览器(IE除外)中也支持dataURI?
jayarjo 2014年

1
@jayarjo data:-Web Worker的URI在Firefox中也受支持,但在Chrome或Opera 15+中不支持。的性能eval无关紧要,您不会每秒创建数百万的Web Worker。
罗布W

12

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

因此,可选地,我们可以使用以下方法将工人作为函数,然后隐式转换为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.

已在IE11 +和FF及Chrome中测试


1
@SenJacob因为这不是社区Wiki帖子,所以您应该通过评论而不是编辑来向海报公开潜在问题。
再见StackExchange

@FrankerZ抱歉。我必须通过所做的更改使其在IE11中工作。@ ChanuSukarno您能否检查版本3中的更改是否正常?
森·雅各布

仅供参考,“类型:'application / javascript; charset = utf-8'”属于Blob构造函数,而不是createObjectURL调用。
Sora2455

那么...您是在worker之外构建函数,只是为了在文本编辑器中更好地阅读它?这是荒谬的。您必须无缘无故地在两个上下文中将该函数加载到内存中。
ADJenks

4

我采用了您的大多数想法,并添加了一些想法。我的代码对工作人员唯一需要的就是使用“ 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/


2

很好的答案-今天,当我尝试创建不具有后备功能的Web Worker时(例如在主线程中运行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>


1

根据您的用例,您可以使用类似

task.js简化的界面,用于使CPU密集型代码在所有内核(node.js和Web)上运行

一个例子是

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

1

扩展@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);
  });
}

1

由于支持向后兼容性,因此可接受的答案有点复杂,因此我想发布相同的内容,但进行了简化。在(现代)浏览器控制台中尝试以下操作:

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.


-1

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

这里是一个背部和往复转换text/javascriptblobobjectURLblobtext/javascript

如果您想知道,我使用它来生成没有外部文件 网络工作者,
您可以使用它来返回二进制内容,例如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 using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.