【问题标题】:How to create a Web Worker from a string如何从字符串创建 Web Worker
【发布时间】:2022-05-06 09:07:00
【问题描述】:

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

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

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

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

这里还有一个帖子discussing it on the whatwg

【问题讨论】:

标签: javascript web-worker data-uri


【解决方案1】:

总结

  • 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 工作者。可以使用 BlobBuilder API deprecatedBlob constructor 创建 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');

兼容性

以下浏览器支持网络工作者source

  • 铬 3
  • 火狐3.5
  • IE 10
  • Opera 10.60
  • Safari 4

此方法的支持基于Blob API 和URL.createObjectUrl 方法的支持。 Blob compatibility:

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

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

Opera 12 不支持URL API。由于this hack in browser.js,一些用户可能拥有URL 对象的伪造版本。

后备 1:数据 URI

Opera 支持将 data-URI 作为 Worker 构造函数的参数。注意:不要忘记escape special characters(如#%)。

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

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

【讨论】:

  • @BrianFreid 感谢您的编辑,但这不是必需的。如果您再看几行,您会看到“IE10 支持MSBlobBuilderURL.createObjectURL。但是,尝试从blob:-URL 创建Web Worker 会引发SecurityError。”。因此,添加MSBlobBuilder 将无效,唯一的选择是回退#2。
  • Opera 12 不再定义 URL (因此也没有定义任何属性),现在 Blob 构造函数得到了足够好的支持。
  • 我已经证实这仍然发生在 IE11 中,至少在预览中。
  • 是否只有 Opera 或所有其他浏览器(IE 除外)也支持 dataURI?
  • @jayarjo data:- Firefox 也支持 Web Workers 的 URI,但 Chrome 或 Opera 15+ 不支持。 eval 的性能无关紧要,您不会每秒创建数百万个 Web 工作者。
【解决方案2】:

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

因此,我们可以选择使用下面的方法,将 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 中测试的

【讨论】:

  • @SenJacob 由于这不是社区 wiki 帖子,您应该通过评论而不是编辑将潜在问题暴露给发布者。
  • @FrankerZ 对不起。我必须通过我所做的更改使其在 IE11 中工作。 @ChanuSukarno 请检查revision 3 中的更改是否正常?
  • 仅供参考,"type: 'application/javascript; charset=utf-8'" 属于 Blob 构造函数,而不是 createObjectURL 调用。
  • 所以...您在工作人员之外构建一个函数,只是为了在您的文本编辑器中更好地阅读它?这是荒谬的。您必须无缘无故地在两个上下文中将该函数加载到内存中。
  • 如何使用它,但将参数传递给函数workerFunction 以用于生成代码?
【解决方案3】:

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

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.

【讨论】:

    【解决方案4】:

    我已经根据您的大部分想法制定了一种方法,并添加了一些我的想法。我的代码在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/

    【讨论】:

      【解决方案5】:

      很好的答案 - 我今天在尝试创建具有后备功能的 Web Workers 不可用时(即在主线程中运行工作脚本)时一直在解决类似的问题。由于此线程与该主题有关,我想我会在这里提供我的解决方案:

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          扩展@Chanu_Sukarno 的代码,您可以简单地将一个工作函数(或字符串)传递给这个函数,它会在一个网络工作者中执行它:

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

          【讨论】:

            【解决方案8】:

            我的解决方案(可以很容易地“承诺”...)

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

            可以很好地使用“纯和慢”功能!

            【讨论】:

              【解决方案9】:

              基于接受的答案、对工人的班级、有趣的话题

              // worker class
              class SimpleWorker {
              
                  constructor() {
                      console.log("simple worker init");
                  }
              
                  onMessage(event) {
                      console.log("main to worker", event.data);
              
                      postMessage({
                          type: "answer", 
                          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);

              【讨论】:

                【解决方案10】:

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

                这是text/javascriptblobobjectURL 再到blobtext/javascript来回转换

                如果您想知道,我正在使用它来生成 没有外部文件的网络工作者
                您可以使用它返回二进制内容,例如 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)}'
                */
                

                【讨论】:

                  【解决方案11】:

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

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

                  但你也可以给它一个功能。

                  【讨论】:

                    猜你喜欢
                    • 2011-04-25
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多