Cách tạo Web Worker từ một chuỗi


83

Làm cách nào để sử dụng tính năng tạo Web worker từ một chuỗi (được cung cấp thông qua yêu cầu POST)?

Một cách tôi có thể nghĩ ra, nhưng tôi không chắc chắn về cách triển khai nó, đó là tạo một data-URI từ phản hồi của máy chủ và chuyển nó đến phương thức khởi tạo Worker, nhưng tôi nghe nói rằng một số trình duyệt không cho phép điều này, vì chính sách xuất xứ giống nhau.

MDN cho biết sự không chắc chắn về chính sách nguồn gốc xung quanh dữ liệu của URI :

Lưu ý: URI được truyền dưới dạng tham số của phương thức khởi tạo Worker phải tuân theo chính sách gốc. Hiện có sự bất đồng giữa các nhà cung cấp trình duyệt về việc liệu các URI dữ liệu có cùng nguồn gốc hay không; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) trở lên cho phép các URI dữ liệu như một tập lệnh hợp lệ cho công nhân. Các trình duyệt khác có thể không đồng ý.

Đây cũng là một bài đăng thảo luận về nó trên whatwg .


Tôi tự hỏi liệu CORS ( w3.org/TR/cors ) có giúp được gì không. HTMl5rocks sử dụng ngôn ngữ "phải" mạnh mẽ khi nói đến chính sách nguồn gốc giống nhau cho người lao động ( html5rocks.com/en/tutorials/workers/basics ) nên có thể CORS không giúp ích được nhiều ở đây. Bạn đã thử nó mặc dù?
Pavel Veller

Câu trả lời:


144

Tóm lược

  • blob: dành cho Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+
  • data:application/javascript cho Opera 10.60 - 12
  • eval nếu không (IE 10+)

URL.createObjectURL(<Blob blob>)có thể được sử dụng để tạo một Web worker từ một chuỗi. Blob có thể được tạo bằng cách sử dụng BlobBuilderAPI không dùng nữa hoặc hàm Blobtạo .

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

Khả năng tương thích

Web worker được hỗ trợ trong nguồn trình duyệt sau :

  • Chrome 3
  • Firefox 3.5
  • IE 10
  • Opera 10.60
  • Safari 4

Hỗ trợ của phương pháp này dựa trên sự hỗ trợ của BlobAPI và URL.createObjectUrlphương thức. Blobkhả năng tương thích :

  • Chrome 8+ ( WebKitBlobBuilder), 20+ (hàm Blobtạo)
  • Firefox 6+ ( MozBlobBuilder), 13+ (hàm Blobtạo)
  • Safari 6+ (hàm Blobtạo)

IE10 hỗ trợ MSBlobBuilderURL.createObjectURL. Tuy nhiên, việc cố gắng tạo Web Worker từ blob:-URL sẽ gây ra lỗi SecurityError.

Opera 12 không hỗ trợ URLAPI. Một số người dùng có thể có phiên bản giả mạo của URLđối tượng, nhờ vào cách hack nàybrowser.js .

Dự phòng 1: data-URI

Opera hỗ trợ data-URI như một đối số cho hàm Workertạo. Lưu ý: Đừng quên thoát các ký tự đặc biệt (Chẳng hạn như #%).

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

Dự phòng 2: Đánh giá

eval có thể được sử dụng làm dự phòng cho Safari (<6) và 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 Cảm ơn bạn đã chỉnh sửa, nhưng nó không cần thiết. Nếu bạn nhìn xa hơn một vài dòng, bạn sẽ thấy "IE10 hỗ trợ MSBlobBuilderURL.createObjectURL. Tuy nhiên, việc cố gắng tạo Web Worker từ blob:-URL sẽ phát sinh lỗi SecurityError.". Vì vậy, việc thêm MSBlobBuildersẽ không có tác dụng, lựa chọn duy nhất là dự phòng # 2.
Rob W

Opera 12 không còn định nghĩa nữa URL(và do đó không định nghĩa bất kỳ thuộc tính nào trên nó), và hàm tạo Blob ngày nay đã được hỗ trợ đủ tốt.
gsnedders

2
Tôi đã xác minh rằng điều này vẫn xảy ra trong IE11, ít nhất là trong bản xem trước.
Benjamin Gruenbaum

1
DataURIs chỉ được hỗ trợ trong Opera hoặc trong tất cả các trình duyệt khác (ngoại trừ IE)?
jayarjo

1
@jayarjo data:-URIs dành cho Nhân viên web cũng được hỗ trợ trong Firefox, nhưng không được hỗ trợ trong Chrome hoặc Opera 15+. Hiệu suất của evalkhông liên quan, bạn sẽ không tạo ra hàng triệu nhân viên Web mỗi giây.
Rob W

12

Tôi đồng ý với câu trả lời được chấp nhận hiện tại nhưng thường thì việc chỉnh sửa và quản lý mã worker sẽ bận rộn vì nó ở dạng chuỗi.

Vì vậy, theo tùy chọn, chúng ta có thể sử dụng cách tiếp cận dưới đây, nơi chúng ta có thể giữ worker dưới dạng một hàm, sau đó ẩn vào 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.

Điều này được thử nghiệm trong IE11 + và FF và Chrome


1
@SenJacob Vì đây không phải là bài đăng trên wiki cộng đồng, bạn nên đưa ra các vấn đề tiềm ẩn cho người đăng thông qua nhận xét thay vì chỉnh sửa.
Tạm biệt StackExchange

@FrankerZ Xin lỗi. Tôi phải làm cho nó hoạt động trong IE11 với những thay đổi tôi đã làm. @ ChanuSukarno Bạn vui lòng kiểm tra xem các thay đổi trong bản sửa đổi 3 có ổn không?
Sen Jacob

FYI, "type: 'application / javascript; charset = utf-8'" thuộc về hàm tạo Blob, không phải lệnh gọi createObjectURL.
Sora2455

Vì vậy, ... Bạn đang xây dựng một chức năng bên ngoài nhân viên của mình, chỉ để đọc nó tốt hơn trong trình soạn thảo văn bản của bạn? Thật là nực cười. Bạn phải tải hàm đó vào bộ nhớ trong hai ngữ cảnh mà không có lý do gì.
ADJenks

4

Tôi đã thực hiện một cách tiếp cận với hầu hết các ý tưởng của bạn và thêm một số ý tưởng của tôi. Điều duy nhất mà mã của tôi cần trên worker là sử dụng 'this' để chỉ phạm vi 'self'. Tôi khá chắc rằng điều này rất ngẫu hứng:

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


2

Câu trả lời rất hay - Hôm nay tôi đang giải quyết một vấn đề tương tự khi cố gắng tạo Nhân viên web với khả năng dự phòng khi chúng không khả dụng (tức là chạy tập lệnh công nhân trong luồng chính). Vì chủ đề này liên quan đến chủ đề, tôi nghĩ tôi sẽ cung cấp giải pháp của mình ở đây:

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


2

Câu trả lời được chấp nhận là một chút phức tạp, do hỗ trợ khả năng tương thích ngược, vì vậy tôi muốn đăng điều tương tự nhưng được đơn giản hóa. Hãy thử điều này trong bảng điều khiển trình duyệt (hiện đại) của bạn:

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

Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể sử dụng một số thứ như

task.js Giao diện đơn giản để lấy mã chuyên sâu của CPU để chạy trên tất cả các lõi (node.js và web)

Một ví dụ sẽ là

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

Mở rộng trên mã của @ Chanu_Sukarno, bạn có thể chỉ cần chuyển một hàm worker (hoặc chuỗi) vào hàm này và nó sẽ thực thi nó bên trong một 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);
  });
}

Đây là một ví dụ về cách sử dụng nó:

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


Trong nodejs , doWorkerTasktrông giống như sau:

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

Bạn có thể nhận thực dữ liệu từ objectURL và không chỉ blob bằng cách thay đổi responseTypemột trong hai "text"hoặc "arraybuffer".

Đây là một chuyển đổi back-và-ra của text/javascriptđể blobđể objectURLtrở lại blobhay text/javascript.

nếu bạn đang thắc mắc, tôi đang sử dụng nó để tạo web-worker không có tệp bên ngoài,
bạn có thể sử dụng nó để trả về nội dung nhị phân, ví dụ: video YouTube;) (từ thuộc tính tài nguyên thẻ <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)}'
*/

-1

Sử dụng plugin nhỏ của tôi https://github.com/zevero/worker-create

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

Nhưng bạn cũng có thể cung cấp cho nó một chức năng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.