Як створити веб-працівника із рядка


83

Як я можу використовувати створення веб-працівника із рядка (який надається через запит POST)?

Одним із способів, про які я можу подумати, але я не впевнений, як це реалізувати, є створення URI даних із відповіді сервера та передача його конструктору Worker, але я чув, що деякі браузери не дозволяють це через ту саму політику походження.

MDN заявляє про невизначеність щодо політики походження щодо URI даних :

Примітка: URI, переданий як параметр конструктора Worker, повинен відповідати політиці того самого походження. В даний час серед продавців браузерів існують розбіжності щодо того, чи мають 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 тут не надто допомагає. Ти все-таки спробував?
Павел Веллер,

Відповіді:


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>)може використовуватися для створення веб-працівника із рядка. Blob можна створити за допомогою застарілогоBlobBuilder API або конструктора .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');

Сумісність

Веб-працівники підтримуються в наступному джерелі браузерів :

  • Chrome 3
  • Firefox 3.5
  • IE 10
  • Опера 10.60
  • Сафарі 4

Підтримка цього методу базується на підтримці BlobAPI та URL.createObjectUrlметоду. Blobсумісність :

  • Chrome 8+ ( WebKitBlobBuilder), 20+ ( Blobконструктор)
  • Firefox 6+ ( MozBlobBuilder), 13+ ( Blobконструктор)
  • Safari 6+ ( Blobконструктор)

IE10 підтримує MSBlobBuilderі URL.createObjectURL. Однак спроба створити веб-працівника з blob:-URL видає помилку 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

3
@BrianFreid Дякуємо за редагування, але воно не потрібне. Якщо поглянути на кілька рядків далі, ви побачите "IE10 підтримує MSBlobBuilderі URL.createObjectURL. Однак спроба створити веб-працівника з blob:-URL видає помилку SecurityError.". Отже, додавання MSBlobBuilderне матиме ніякого ефекту, єдиним варіантом є запасний номер 2.
Роб W

Opera 12 більше не визначає URL(і, отже, не визначає жодних властивостей на ньому), і конструктор Blob в даний час досить добре підтримується.
gsnedders

2
Я перевірив, що це все ще відбувається в IE11, принаймні в попередньому перегляді.
Бенджамін Груенбаум,

1
Чи підтримуються dataURI лише в Opera або у всіх інших браузерах (крім IE)?
jayarjo

1
@jayarjo data:-URI для веб-працівників також підтримуються у Firefox, але не в Chrome або Opera 15+. Продуктивність evalне є важливою, ви не збираєтеся створювати мільйони веб-працівників за секунду.
Роб 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 Оскільки це не вікі-публікація спільноти, вам слід виставляти потенційні проблеми плакату через коментар, замість редагування.
До побачення StackExchange

@FrankerZ Вибачте. Мені довелося змусити це працювати в IE11 із внесеними змінами. @ ChanuSukarno Не могли б ви, будь ласка, перевірити, чи внесені зміни до редакції 3 в порядку?
Sen Jacob

FYI, тип: 'application / javascript; charset = utf-8' "належить у конструкторі Blob, а не виклику createObjectURL.
Sora2455

Отже ... Ви будуєте функцію поза вашим працівником, аби просто прочитати її краще у текстовому редакторі? Це смішно. Вам доведеться завантажувати цю функцію в пам’ять у двох контекстах без жодної причини.
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 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>


2

Прийнята відповідь дещо складна через підтримку зворотної сумісності, тому я хотів опублікувати те саме, але спрощений. Спробуйте це на своїй (сучасній) консолі браузера:

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

Залежно від випадку використання ви можете використовувати щось на зразок

task.js Спрощений інтерфейс для отримання інтенсивного процесорного коду для роботи на всіх ядрах (node.js та веб)

Прикладом може бути

// 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, ви можете просто передати робочу функцію (або рядок) цій функції, і вона виконає її всередині веб-працівника:

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

Ви можете отримати реальні дані з objectURL, а не просто BLOB, змінивши значення responseTypeна "text"або "arraybuffer".

Ось перетворення назад і вперед від , text/javascriptщоб blobна objectURLспині до blobабо text/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)}'
*/

-1

Використовуйте мій крихітний плагін https://github.com/zevero/worker-create

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

Але ви можете також надати йому функцію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.