如何从字符串创建 Web Worker

IT技术 javascript web-worker data-uri
2021-02-10 05:00:14

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

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

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

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

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

6个回答

概括

  • 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
  • 火狐 3.5
  • 浏览器 10
  • 歌剧 10.60
  • 野生动物园 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的对象,这要归功于这个技巧在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
dataURI 是否仅在 Opera 或所有其他浏览器(IE 除外)中支持?
2021-03-22 05:00:14
我已经验证这仍然发生在 IE11 中,至少在预览中。
2021-03-23 05:00:14
Opera 12 不再定义URL(因此也没有在其上定义任何属性),现在 Blob 构造函数得到了足够的支持。
2021-04-04 05:00:14
@BrianFreid 感谢您的编辑,但这不是必需的。如果您再看几行,您将看到“IE10 支持MSBlobBuilderURL.createObjectURL。但是,尝试从blob:-URL创建 Web Worker会引发 SecurityError。”。因此,添加MSBlobBuilder将不起作用,唯一的选择是回退 #2。
2021-04-11 05:00:14
@jayarjo - data:Firefox 也支持 Web Workers 的 URI,但 Chrome 或 Opera 15+ 不支持。的性能eval无关紧要,您不会每秒创建数百万个 Web 工作者。
2021-04-11 05:00:14

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

因此,我们可以选择使用以下方法,我们可以将 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.

这是在 IE11+ 和 FF 以及 Chrome 中测试的

所以......你正在你的工作人员之外构建一个函数,只是为了在你的文本编辑器中更好地阅读它?这是荒谬的。您必须在两种情况下无缘无故地将该函数加载到内存中。
2021-03-20 05:00:14
@FrankerZ 对不起。我必须通过我所做的更改使其在 IE11 中工作。@ ChanuSukarno 能否请您检查一下修订版 3 中的更改是否正常?
2021-03-29 05:00:14
@SenJacob 由于这不是社区 wiki 帖子,您应该通过评论而不是编辑向发布者公开潜在问题。
2021-04-03 05:00:14
您如何使用它,但将参数传递给函数workerFunction以用于生成代码?
2021-04-04 05:00:14
仅供参考,“type: 'application/javascript; charset=utf-8'”属于 Blob 构造函数,而不是 createObjectURL 调用。
2021-04-05 05:00:14

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

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