Веб-работники без отдельного файла Javascript? - PullRequest
270 голосов
/ 23 марта 2011

Насколько я могу судить, веб-работники должны быть написаны в отдельном файле JavaScript и называться так:

new Worker('longrunning.js')

Я использую компилятор закрытия для объединения и минимизации всего моего исходного кода JavaScript, и я бы не хотел, чтобы мои работники размещались в отдельных файлах для распространения. Есть ли способ сделать это?

new Worker(function() {
    //Long-running work here
});

Учитывая, что первоклассные функции так важны для JavaScript, почему стандартный способ выполнения фоновой работы должен загружать целый «другой» файл JavaScript с сервера?

Ответы [ 24 ]

0 голосов
/ 20 февраля 2019

Другое решение - просто обернуть Worker в функцию, а затем создать BLOB-объект, вызывающий функцию следующим образом:

     function workerCode() {
        self.onmessage = function (e) {
          console.log("Got message from parent", e.data);
        };
        setTimeout(() => {
          self.postMessage("Message From Worker");
        }, 2000);
      }

      let blob = new Blob([
        "(" + workerCode.toString() + ")()"
      ], {type: "text/javascript"});

      // Note: window.webkitURL.createObjectURL() in Chrome 10+.
      let worker = new Worker(window.URL.createObjectURL(blob));
      worker.onmessage = function (e) {
        console.log("Received: " + e.data);
      };
      worker.postMessage("hello"); // Start the worker.
0 голосов
/ 31 августа 2018

Вы можете поместить содержимое вашего файла worker.js внутри обратных кавычек (что позволяет использовать многострочную строковую константу) и создать работника из большого двоичного объекта следующим образом:

var workerScript = `
    self.onmessage = function(e) {
        self.postMessage('message from worker');
    };
    // rest of worker code goes here
`;

var worker =
    new Worker(createObjectURL(new Blob([workerScript], { type: "text/javascript" })));

Это удобно, если по какой-либо причине вы не хотите иметь отдельные теги сценария для работника.

0 голосов
/ 11 ноября 2017

Я использую такой код, вы можете определить свое сообщение как функцию, отличную от простого текста, чтобы редактор мог выделить ваш код, и jshint работает.

const worker = createWorker();

createWorker() {
    const scriptContent = getWorkerScript();
    const blob = new Blob([
        scriptContent,
    ], {
        type: "text/javascipt"
    });
    const worker = new Worker(window.URL.createObjectURL(blob));
    return worker;
}

getWorkerScript() {
    const script = {
        onmessage: function (e) {
            console.log(e);
            let result = "Hello " + e.data
            postMessage(result);
        }
    };
    let content = "";
    for (let prop in script){
        content += `${prop}=${script[prop].toString()}`;
    }
    return content;
}
0 голосов
/ 14 декабря 2017

Да, возможно, я сделал это, используя файлы Blob и передав обратный вызов

Я покажу вам, что делает класс, который я написал, и как он управляет выполнением обратных вызовов в фоновом режиме.

Сначала вы создаете экземпляр GenericWebWorker с любыми данными, которые вы хотите передать обратному вызову, которые будут выполняться в Web Worker, который включает функции, которые вы хотите использовать, в данном случае число, дату и функция называется blocker

var worker = new GenericWebWorker(100, new Date(), blocker)

Эта блокирующая функция будет выполняться бесконечно, в течение n миллисекунд

function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

и затем вы используете это так

worker.exec((num, date, fnBlocker) => {
    /*Everithing here does not block the main thread
      and this callback has access to the number, date and the blocker */
    fnBlocker(10000) //All of this run in backgrownd
    return num*10

}).then(d => console.log(d)) //Print 1000

Теперь пришло время увидеть магию в приведенном ниже примере

/*https://github.com/fercarvo/GenericWebWorker*/
class GenericWebWorker {
    constructor(...ags) {
        this.args = ags.map(a => (typeof a == 'function') ? {type:'fn', fn:a.toString()} : a)
    }

    async exec(cb) {
        var wk_string = this.worker.toString();
        wk_string = wk_string.substring(wk_string.indexOf('{') + 1, wk_string.lastIndexOf('}'));            
        var wk_link = window.URL.createObjectURL( new Blob([ wk_string ]) );
        var wk = new Worker(wk_link);

        wk.postMessage({ callback: cb.toString(), args: this.args });
 
        var resultado = await new Promise((next, error) => {
            wk.onmessage = e => (e.data && e.data.error) ? error(e.data.error) : next(e.data);
            wk.onerror = e => error(e.message);
        })

        wk.terminate(); window.URL.revokeObjectURL(wk_link);
        return resultado
    }

    async parallel(arr, cb) {
        var res = [...arr].map(it => new GenericWebWorker(it, ...this.args).exec(cb))
        var all = await Promise.all(res)
        return all
    }

    worker() {
        onmessage = async function (e) {
            try {                
                var cb = new Function(`return ${e.data.callback}`)();
                var args = e.data.args.map(p => (p.type == 'fn') ? new Function(`return ${p.fn}`)() : p);

                try {
                    var result = await cb.apply(this, args); //If it is a promise or async function
                    return postMessage(result)

                } catch (e) { throw new Error(`CallbackError: ${e}`) }
            } catch (e) { postMessage({error: e.message}) }
        }
    }
}


function blocker (ms) {
    var now = new Date().getTime();
    while(true) {
        if (new Date().getTime() > now +ms)
            return;
    }   
}

setInterval(()=> console.log("Not blocked " + Math.random()), 1000)

console.log("\n\nstarting blocking code in Worker\n\n")

var worker = new GenericWebWorker(100, new Date(), blocker)

worker.exec((num, date, fnBlocker) => {
    fnBlocker(7000) //All of this run in backgrownd
    return num*10    
})
.then(d => console.log(`\n\nEnd of blocking code: result ${d}\n\n`)) //Print 1000
...