Pracownicy sieci bez osobnego pliku JavaScript?

291

O ile mi wiadomo, pracownicy sieci muszą być zapisani w osobnym pliku JavaScript i nazywani w ten sposób:

new Worker('longrunning.js')

Używam kompilatora zamykającego do łączenia i minimalizowania całego kodu źródłowego JavaScript i wolałbym nie mieć swoich pracowników w osobnych plikach do dystrybucji. Czy jest jakiś sposób, aby to zrobić?

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

Biorąc pod uwagę, że pierwszorzędne funkcje są tak ważne dla JavaScript, dlaczego standardowy sposób wykonywania pracy w tle musi ładować cały inny plik JavaScript z serwera WWW?

Ben Dilts
źródło
7
To dlatego, że zachowanie kontekstu czysto bezpiecznego dla wątków jest nawet ważniejsze niż funkcje pierwszej klasy :-)
Pointy
1
Pracuję nad tym (a raczej nad zminimalizowaniem problemu): DynWorker . Możesz zrobić: var worker = new DynWorker(); worker.inject("foo", function(){...});...
Félix Saparelli,
1
OP usunął pytanie „Nauczyciel akceptujący funkcję zamiast pliku źródłowego JavaScript”. Odpowiedź jest opublikowana tutaj
Rob W
Opracowałem task.js, aby było to o wiele łatwiejsze. Przez większość czasu próbujesz odciążyć tylko małe zadania blokowania.
Chad Scira

Odpowiedzi:

225

http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers

Co zrobić, jeśli chcesz utworzyć skrypt roboczy w locie lub utworzyć niezależną stronę bez konieczności tworzenia osobnych plików roboczych? Za pomocą Blob () możesz „wstawić” swojego pracownika do tego samego pliku HTML, co główna logika, tworząc uchwyt adresu URL do kodu pracownika jako ciąg


Pełny przykład wbudowanego pracownika BLOB:

<!DOCTYPE html>
<script id="worker1" type="javascript/worker">
  // This script won't be parsed by JS engines because its type is javascript/worker.
  self.onmessage = function(e) {
    self.postMessage('msg from worker');
  };
  // Rest of your worker code goes here.
</script>
<script>
  var blob = new Blob([
    document.querySelector('#worker1').textContent
  ], { type: "text/javascript" })

  // Note: window.webkitURL.createObjectURL() in Chrome 10+.
  var worker = new Worker(window.URL.createObjectURL(blob));
  worker.onmessage = function(e) {
    console.log("Received: " + e.data);
  }
  worker.postMessage("hello"); // Start the worker.
</script>

vsync
źródło
Tylko rozwiązanie dla Google Chrome, wygląda na to, że Firefox 10 będzie go obsługiwał, nie wiem o innych przeglądarkach
4esn0k
2
BlobBuiler jest teraz przestarzały . Zamiast tego użyj obiektu Blob . Obecnie obsługiwane w najnowszych przeglądarkach Firefox / WebKit / Opera i IE10, zobacz tabele kompatybilności dla starszych przeglądarek.
Félix Saparelli,
3
Konstruktor blob może być obsługiwane w IE10, ale nadal nie można przekazać javascript pracownikowi internetowej przez niego (nawet w IE11): connect.microsoft.com/IE/feedback/details/801810/... .
jayarjo
1
@albanx - jakie testy? w Internecie jest już miliard stron demonstracyjnych, które pokazują, że wątki nie zawieszają przeglądarki przez lata.
vsync
2
@albanx - czy chciałbyś przynajmniej powiedzieć, której ezoterycznej przeglądarki używasz, a która zawiesza się? czy to demo wisi dla ciebie? ie.microsoft.com/testdrive/Graphics/WorkerFountains/…
vsync
162

Rozwiązanie HTML5rocks polegające na osadzaniu kodu roboczego w HTML jest dość okropne.
A kropla uciekającego kodu JavaScript jako ciąg znaków nie jest lepsza, zwłaszcza dlatego, że komplikuje przepływ pracy (kompilator Closure nie może działać na ciągach znaków).

Osobiście bardzo lubię metody toString, ale @ dan-man TO wyrażenie regularne!

Moje preferowane podejście:

// Build a worker from an anonymous function body
var blobURL = URL.createObjectURL( new Blob([ '(',

function(){
    //Long-running work here
}.toString(),

')()' ], { type: 'application/javascript' } ) ),

worker = new Worker( blobURL );

// Won't be needing this anymore
URL.revokeObjectURL( blobURL );

Wsparcie to przecięcie tych trzech tabel:

Nie będzie to jednak działać dla SharedWorker , ponieważ adres URL musi być dokładnie zgodny, nawet jeśli opcjonalny parametr „name” pasuje. Dla SharedWorker potrzebujesz osobnego pliku JavaScript.


Aktualizacja 2015 - Nadchodzi osobliwość ServiceWorker

Teraz istnieje jeszcze skuteczniejszy sposób rozwiązania tego problemu. Ponownie przechowuj kod roboczy jako funkcję (zamiast ciągu statycznego) i konwertuj za pomocą .toString (), a następnie wstaw kod do CacheStorage pod wybranym statycznym adresem URL.

// Post code from window to ServiceWorker...
navigator.serviceWorker.controller.postMessage(
 [ '/my_workers/worker1.js', '(' + workerFunction1.toString() + ')()' ]
);

// Insert via ServiceWorker.onmessage. Or directly once window.caches is exposed
caches.open( 'myCache' ).then( function( cache )
{
 cache.put( '/my_workers/worker1.js',
  new Response( workerScript, { headers: {'content-type':'application/javascript'}})
 );
});

Możliwe są dwa awarie. ObjectURL jak wyżej lub bardziej płynnie, umieść prawdziwy plik JavaScript na /my_workers/worker1.js

Zalety tego podejścia to:

  1. SharedWorkers może być również obsługiwany.
  2. Karty mogą udostępniać jedną kopię z pamięci podręcznej pod stałym adresem. Metoda BLOB namnaża losowe adresy URL obiektów dla każdej karty.
Adria
źródło
4
Jak wyglądałaby kompatybilność przeglądarki w tym rozwiązaniu?
Ben Dilts,
Czy możesz rozwinąć to rozwiązanie, jak ono działa? Co to jest worker1.js? Czy to osobny plik js? Próbuję tego użyć, ale nie mogę go uruchomić. W szczególności staram się, aby działał dla SharedWorker
Yehuda
Gdybyś tylko mógł zawinąć to w przydatną funkcję!
mmm
@ Ben Dilts: Kompatybilność z przeglądarkami wyglądałaby tak, jakbyś uruchamiał twój kod poprzez babel: babeljs.io/repl
Jack Giffin
Standard nie gwarantuje, że Function.prototype.toString () zwraca ciało funkcji jako ciąg znaków. Prawdopodobnie powinieneś dodać ostrzeżenie do odpowiedzi.
RD
37

Możesz utworzyć pojedynczy plik JavaScript, który jest świadomy jego kontekstu wykonywania i może działać zarówno jako skrypt nadrzędny, jak i proces roboczy. Zacznijmy od podstawowej struktury pliku takiego jak ten:

(function(global) {
    var is_worker = !this.document;
    var script_path = is_worker ? null : (function() {
        // append random number and time to ID
        var id = (Math.random()+''+(+new Date)).substring(2);
        document.write('<script id="wts' + id + '"></script>');
        return document.getElementById('wts' + id).
            previousSibling.src;
    })();
    function msg_parent(e) {
        // event handler for parent -> worker messages
    }
    function msg_worker(e) {
        // event handler for worker -> parent messages
    }
    function new_worker() {
        var w = new Worker(script_path);
        w.addEventListener('message', msg_worker, false);
        return w;
    }
    if (is_worker)
        global.addEventListener('message', msg_parent, false);

    // put the rest of your library here
    // to spawn a worker, use new_worker()
})(this);

Jak widać, skrypt zawiera cały kod zarówno z punktu widzenia rodzica, jak i pracownika, sprawdzając, czy jego własna indywidualna instancja jest robotnikiem !document. Nieco nieporęczne script_pathobliczenia służą do dokładnego obliczenia ścieżki skryptu względem strony nadrzędnej, ponieważ podana ścieżka new Workerjest względem strony nadrzędnej, a nie skryptu.

Delan Azabani
źródło
4
Twoja witryna prawdopodobnie zniknęła; masz nowy adres URL?
BrianFreud
1
To interesujące podejście. FWIW, wykrywam pracowników sieci Web, sprawdzając obecność „siebie” (obiektu globalnego pracownika sieci) w porównaniu z „oknem”.
pwnall
Patrzyłem na to, jak PapaParse radzi sobie z pracownikami sieci i wydaje się, że przyjmują takie podejście github.com/mholt/PapaParse
JP DeVries
Myślę, że testowanie przy użyciu „typeof importScripts! == null” pozwala stwierdzić, czy skrypt działa w zakresie roboczym.
MeTTeO
1
Nie rozumiem, czym jest previousSibling z elementu skryptu. Czy ktoś może mi wyjaśnić?
Teemoh
28

Korzystając z Blobmetody, co powiesz na to dla fabryki pracowników:

var BuildWorker = function(foo){
   var str = foo.toString()
             .match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
   return  new Worker(window.URL.createObjectURL(
                      new Blob([str],{type:'text/javascript'})));
}

Możesz więc użyć go w ten sposób ...

var myWorker = BuildWorker(function(){
   //first line of worker
   self.onmessage(){....};
   //last line of worker
});

EDYTOWAĆ:

Właśnie rozszerzyłem ten pomysł, aby ułatwić komunikację między wątkami: bridged-worker.js .

EDYCJA 2:

Powyższy link prowadzi do utworzonej przeze mnie treści. Ktoś później przekształcił go w rzeczywiste repo .

dan-man
źródło
11

Pracownicy sieci pracują w całkowicie odrębnych kontekstach jako poszczególne programy.

Oznacza to, że kodu nie można przenosić z jednego kontekstu do drugiego w formie obiektowej, ponieważ mogłyby one wówczas odwoływać się do obiektów poprzez zamknięcia należące do innego kontekstu.
Jest to szczególnie ważne, ponieważ ECMAScript został zaprojektowany jako język jednowątkowy, a ponieważ pracownicy sieci pracują w osobnych wątkach, istnieje ryzyko, że zostaną wykonane operacje nieobsługujące wątków.

To znowu oznacza, że ​​pracownicy sieci muszą zostać zainicjowani kodem w postaci źródłowej.

Specyfikacja WHATWG mówi

Jeśli pochodzenie wynikowego bezwzględnego adresu URL nie jest takie samo jak pochodzenie skryptu wejściowego, wygeneruj wyjątek SECURITY_ERR.

Dlatego skrypty muszą być plikami zewnętrznymi o takim samym schemacie jak strona oryginalna: nie można załadować skryptu z danych: URL lub javascript: URL, a strona https: nie mogła uruchomić pracowników korzystających ze skryptów z http: URL.

ale niestety tak naprawdę nie wyjaśnia, dlaczego nie można było pozwolić konstruktorowi przekazać łańcucha z kodem źródłowym.

Sean Kinsey
źródło
6

lepszy sposób na przeczytanie dla pracownika wbudowanego ..

    var worker_fn = function(e) 
    {
        self.postMessage('msg from worker');            
    };

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

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) 
    {
       alert(e.data);
    };
    worker.postMessage("start"); 
Chris Tobba
źródło
To, co zrobiłem, to to, że utworzyłem funkcję z całym kodem toString()roboczym , przekazałem tę funkcję , wyrzuciłem treść, a następnie umieściłem ją w obiekcie Blob. Sprawdź ostatnią odpowiedź, mam przykład
Fernando Carvajal
5

Przyjmowanie odpowiedzi Adrii i włączenie jej do funkcji kopiowalnej, która działa z obecnymi Chrome i FF, ale nie IE10 (pracownik z obiektu blob powoduje błąd bezpieczeństwa ).

var newWorker = function (funcObj) {
    // Build a worker from an anonymous function body
    var blobURL = URL.createObjectURL(new Blob(
        ['(', funcObj.toString(), ')()'],
        {type: 'application/javascript'}
     ));

    var worker = new Worker(blobURL);

    // Won't be needing this anymore
    URL.revokeObjectURL(blobURL);

    return worker;
}

A oto działający przykład http://jsfiddle.net/ubershmekel/YYzvr/

ubershmekel
źródło
5

Ostatnia odpowiedź (2018)

Możesz użyć Greenleta :

Przenieś funkcję asynchroniczną do własnego wątku. Uproszczona, jednofunkcyjna wersja Workerize .

Przykład:

import greenlet from 'greenlet'

const getName = greenlet(async username => {
  const url = `https://api.github.com/users/${username}`
  const res = await fetch(url)
  const profile = await res.json()
  return profile.name
})

console.log(await getName('developit'))
GG
źródło
3

W zależności od przypadku użycia możesz użyć czegoś takiego

task.js Uproszczony interfejs do uruchamiania kodu intensywnego procesora na wszystkich rdzeniach (node.js i sieci)

Przykładem może być

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});
Chad Scira
źródło
2

Spójrz na wtyczkę vkThread. Dzięki wtyczce htis możesz przejąć dowolną funkcję w głównym kodzie i wykonać ją w wątku (pracownik sieciowy). Nie musisz więc tworzyć specjalnego „pliku roboczego”.

http://www.eslinstructor.net/vkthread/

- Vadim

Vadimk
źródło
1

Możesz używać pracowników sieci Web w tym samym pliku JavaScript, używając wbudowanych pracowników sieci.

Poniższy artykuł skieruje Cię do łatwego zrozumienia webworkerów i ich ograniczeń oraz debugowania webmasterów.

Opanowanie w webmasterach

Kirankumar
źródło
1

Myślę, że lepszym sposobem na to jest użycie obiektu Blob, poniżej możesz zobaczyć prosty przykład.

// create a Blob object with a worker code
var blob = new Blob(["onmessage = function(e) { postMessage('msg from worker'); }"]);

// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);

// create a Worker
var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  console.log(e.data);
};
worker.postMessage("Send some Data"); 
Miguel Q.
źródło
1

Spróbuj użyć jThread. https://github.com/cheprasov/jThread

// You can use simple calling like this
jThread(
    function(arr){
        //... some code for Worker
        return arr;
    }
    ,function(arr){
        //... done code
    }
)( [1,2,3,4,5,6,7] ); // some params
Alexander Cheprasov
źródło
1

tutaj konsola:

var worker=new Worker(window.URL.createObjectURL(new Blob([function(){
  //Long-running work here
  postMessage('done');
}.toString().split('\n').slice(1,-1).join('\n')],{type:'text/javascript'})));

worker.addEventListener('message',function(event){
  console.log(event.data);
});
59naga
źródło
1

https://developer.mozilla.org/es/docs/Web/Guide/Performance/Using_web_workers

    // Syntax: asyncEval(code[, listener])

var asyncEval = (function () {

  var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");

  oParser.onmessage = function (oEvent) {
    if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
    delete aListeners[oEvent.data.id];
  };


  return function (sCode, fListener) {
    aListeners.push(fListener || null);
    oParser.postMessage({
      "id": aListeners.length - 1,
      "code": sCode
    });
  };

})();
hamboy75
źródło
1

Użyj mojej małej wtyczki https://github.com/zevero/worker-create

var worker_url = Worker.createURL(function(e){
  self.postMessage('Example post from Worker'); //your code here
});
var worker = new Worker(worker_url);
zevero
źródło
1

Myślę więc, że mamy teraz kolejną fajną opcję, dzięki literałom szablonów w ES6. To pozwala nam zrezygnować z dodatkowej funkcji pracownika (i jej dziwnego zakresu) i po prostu napisać kod przeznaczony dla pracownika jako tekst wielowierszowy, podobnie jak w przypadku, gdy używaliśmy do przechowywania tekstu, ale bez potrzeby posiadania dokumentu lub DOM zrobić to w. Przykład:

const workerScript = `
self.addEventListener('message', function(e) {
  var data = e.data;
  console.log('worker recieved: ',data);
  self.postMessage('worker added! :'+ addOne(data.value));
  self.close();//kills the worker
}, false);
`;

Oto sedno tego podejścia .

Zauważ, że możemy pobrać dowolne dodatkowe zależności funkcji, które chcemy, do robota, po prostu zbierając je do tablicy i uruchamiając .toString na każdym z nich, aby zredukować je również do ciągów (powinny działać, dopóki są deklaracjami funkcji) i a następnie po prostu dodając go do ciągu skryptu. W ten sposób nie musimy importować skryptów, które mogliśmy już włączyć do zakresu pisanego przez nas kodu.

Jedynym prawdziwym minusem tej konkretnej wersji jest to, że linters nie będą w stanie pobrudzić kodu pracownika usługi (ponieważ jest to tylko ciąg znaków), co jest zaletą dla „osobnego podejścia do funkcji pracownika”.

Dtipson
źródło
1

To tylko dodatek do powyższego - mam ładne szablony do testowania robotów WWW w jsFiddle. Zamiast Blob używa jsFiddles ?jsapi:

function workerFN() {
  self.onmessage = function(e) {
    switch(e.data.name) {
      case "" : 
      break;
      default:
        console.error("Unknown message:", e.data.name);
    }
  }
}
// This is a trick to generate real worker script that is loaded from server
var url = "/echo/js/?js="+encodeURIComponent("("+workerFN.toString()+")()");
var worker = new Worker(url);
worker.addEventListener("message", function(e) {
  switch(e.data.name) {
    case "" : 
    break;
    default:
      console.error("Unknown message:", e.data.name);
  }
})

Dostępne są normalne szablony procesów roboczych i udostępnione szablony procesów roboczych .

Tomáš Zato - Przywróć Monikę
źródło
1

Odkryłem, że CodePen obecnie nie wyróżnia <script>znaczników wbudowanych , które nie są type="text/javascript"(lub które nie mają atrybutu type).

Wymyśliłem więc podobne, ale nieco inne rozwiązanie, używając bloków z etykietamibreak , co jest jedynym sposobem na zwolnienie z <script>tagu bez tworzenia funkcji otoki (co jest niepotrzebne).

<!DOCTYPE html>
<script id="worker1">
  worker: { // Labeled block wrapper

    if (typeof window === 'object') break worker; // Bail if we're not a Worker

    self.onmessage = function(e) {
      self.postMessage('msg from worker');
    };
    // Rest of your worker code goes here.
  }
</script>
<script>
  var blob = new Blob([
    document.querySelector('#worker1').textContent
  ], { type: "text/javascript" })

  // Note: window.webkitURL.createObjectURL() in Chrome 10+.
  var worker = new Worker(window.URL.createObjectURL(blob));
  worker.onmessage = function(e) {
    console.log("Received: " + e.data);
  }
  worker.postMessage("hello"); // Start the worker.
</script>

msanford
źródło
1

Prosta wersja obiecana Function#callAsWorker, która przyjmuje thisArg i argumenty (podobnie jak call) i zwraca obietnicę:

Function.prototype.callAsWorker = function (...args) {
    return new Promise( (resolve, reject) => {
        const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
            blob = new Blob([code], { type: "text/javascript" }),
            worker = new Worker(window.URL.createObjectURL(blob));
        worker.onmessage = e => (resolve(e.data), worker.terminate());
        worker.onerror = e => (reject(e.message), worker.terminate());
        worker.postMessage(args);
    });
}

// Demo
function add(...nums) {
    return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
    console.log('result: ', result);
});

trincot
źródło
powinieneś dodać close()metodę, aby zamknąć hak na życie pracownika sieci. developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/…
Shahar ド ー ン Levi
@Shahar ド ー ン Levi, closefunkcja jest przestarzała. Jednak pracownicy mogą zostać rozwiązani . Dodałem to teraz.
trincot
0

Używam takiego kodu, możesz zdefiniować swój komunikat jako funkcję inną niż zwykły tekst, dzięki czemu edytor może podświetlić kod i działa 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;
}

Z.JC
źródło
Spójrz na moją odpowiedź , właśnie to zrobiłem, ale napisałem całą klasę, aby streścić, jak przekazywać callbacki
Fernando Carvajal
0

Tak, jest to możliwe, zrobiłem to przy użyciu plików Blob i przekazując oddzwonienie

Pokażę ci, co pisze klasa i jak zarządza wykonywaniem wywołań zwrotnych w tle.

Najpierw utwórz instancję GenericWebWorkerz dowolnymi danymi, które chcesz przekazać do wywołania zwrotnego, które będą wykonywane w Web Worker, który obejmuje funkcje, których chcesz użyć, w tym przypadku liczbę, datę i funkcję o nazwieblocker

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

Ta funkcja blokująca będzie wykonywać nieskończoność przez n milisekund

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

a następnie używasz go w ten sposób

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

Czas zobaczyć magię w poniższym przykładzie

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

Fernando Carvajal
źródło
0

Możesz umieścić zawartość pliku worker.js w backticks (co pozwala na stałą ciągu wielowierszowego) i utworzyć pracownika z obiektu blob w następujący sposób:

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

Jest to przydatne, jeśli z jakiegokolwiek powodu nie chcesz mieć oddzielnych znaczników skryptu dla pracownika.

samgak
źródło
0

Innym rozwiązaniem jest po prostu owinięcie Workera w funkcję, a następnie utworzenie obiektu blob wywołującego funkcję w następujący sposób:

     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.
Shlomi Schwartz
źródło
-1

Jednowierszowy do uruchamiania funkcji w pracownikach:

const FunctionalWorker = fn => new Worker(window.URL.createObjectURL(new Blob(["(" + workerCode.toString() + ")()"], {type: "text/javascript"})));

Przykładowe użycie:

let fn = FunctionalWorker(() => {
    self.postMessage("hi");
});
fn.onmessage = msg => {
    console.log(msg);
};
ifeelbadformyoldquestions
źródło