2013-05-23 1 views
68

In che modo angularJS utilizza gli operatori Web per eseguire processi in background? C'è qualche schema che dovrei seguire nel fare questo?AngularJS e web worker

Attualmente sto utilizzando un servizio che ha il modello in un web worker separato. Questo servizio implementa metodi come:

ClientsFacade.calculateDebt(client1); //Just an example.. 

Nell'implementazione, questo metodo invia un messaggio al lavoratore con i dati. Questo mi consente di astrarre il fatto che viene eseguito in un thread separato e potrei anche fornire un'implementazione che si interroga su un server o anche uno che fa questa azione nello stesso thread.

Dal momento che sono nuovo di javascript e sto solo riciclando la conoscenza che ho da altre piattaforme mi chiedo se questo è qualcosa che faresti o forse angolare che è quello che sto usando, offre una sorta di modo di fare questo . Anche questo introduce un cambiamento nella mia architettura poiché il lavoratore deve esplicitamente spingere le modifiche al controller, che quindi aggiorna i suoi valori e quindi questo si riflette nella vista, sto finanziando questo? È un po 'frustrante che i web worker "mi proteggano così tanto da rovinare non permettendomi di condividere la memoria ecc.

risposta

92

La comunicazione con gli operatori Web avviene tramite un meccanismo di messaggistica. Intercettare questi messaggi avviene in una richiamata. In AngularJS, la posizione migliore per mettere un web worker è in un servizio come hai debitamente notato. Il modo migliore per affrontare questo è usare le promesse, con le quali Angular lavora in modo sorprendente.

Ecco un esempio di una webworker in un service

var app = angular.module("myApp",[]); 

app.factory("HelloWorldService",['$q',function($q){ 

    var worker = new Worker('doWork.js'); 
    var defer = $q.defer(); 
    worker.addEventListener('message', function(e) { 
     console.log('Worker said: ', e.data); 
     defer.resolve(e.data); 
    }, false); 

    return { 
     doWork : function(myData){ 
      defer = $q.defer(); 
      worker.postMessage(myData); // Send data to our worker. 
      return defer.promise; 
     } 
    }; 

}); 

Ora, tutto ciò entità che accede il servizio Ciao Mondo non hanno bisogno di cure circa i dettagli di implementazione di HelloWorldService esterno - HelloWorldService probabilmente potrebbe elaborare i dati nel corso di un web worker, oltre http o eseguire l'elaborazione proprio lì.

Spero che questo abbia senso.

+3

ciò che è ** ** doWork.js in ** var lavoratore = nuovo lavoratore ('doWork.js'); **? – acudars

+2

È un riferimento al file js esterno che contiene il codice per il web worker. – ganaraj

+0

Come faccio a fare riferimento a un file con percorsi angolari in atto? In questo momento deve caricare e asincronizzare lo script dal mio server. Posso averlo memorizzato sul client? –

10

Ho trovato un esempio completamente funzionante dei lavoratori web in angolare here

webworker.controller('webWorkerCtrl', ['$scope', '$q', function($scope, $q) { 

    $scope.workerReplyUI; 
    $scope.callWebWorker = function() { 
     var worker = new Worker('worker.js'); 
     var defer = $q.defer(); 
     worker.onmessage = function(e) { 
      defer.resolve(e.data); 
      worker.terminate(); 
     }; 

     worker.postMessage("http://jsonplaceholder.typicode.com/users"); 
     return defer.promise; 
    } 

    $scope.callWebWorker().then(function(workerReply) { 
     $scope.workerReplyUI = workerReply; 
    }); 

}]); 

Usa promette di aspettare per il lavoratore di restituire il risultato.

+3

Il sito che ospita l'esempio non è più disponibile. Ecco il link all'archivio https://web.archive.org/web/20150709233911/http://www.codingterminal.com/articles/092014/1/XMLHttpRequest%20using%20AngularJS%20Promises%20and%20HTML5%20Web%20Workers. php –

15

Una domanda molto interessante! Trovo le specifiche del web worker un po 'scomode (probabilmente per buone ragioni, ma comunque scomode). La necessità di mantenere il codice worker in un file separato rende l'intenzione di un servizio difficile da leggere e introduce dipendenze dagli URL di file statici nel proprio codice angolare dell'applicazione. Questo problema può essere mitigato utilizzando l'URL.createObjectUrl() che può essere utilizzato per creare un URL per una stringa JavaScript. Questo ci permette di specificare il codice worker nello stesso file che crea il worker.

var blobURL = URL.createObjectURL(new Blob([ 
    "var i = 0;//web worker body" 
], { type: 'application/javascript' })); 
var worker = new Worker(blobURL); 

L'operaio spec web mantiene anche il lavoratore e principali contesti filo completamente separati per evitare situazioni erano situazioni di stallo e livelocks ecc possono verificarsi. Ma significa anche che non avrai accesso ai tuoi servizi angolari nel lavoratore senza un po 'di giocherellare. Il lavoratore manca alcune delle cose che noi (e angolari) ci aspettiamo quando eseguiamo JavaScript nel browser, come la variabile globale "documento", ecc. "Mettendo in sesto" queste caratteristiche del browser richieste nel lavoratore possiamo diventare angolari per l'esecuzione.

var window = self; 
self.history = {}; 
var document = { 
    readyState: 'complete', 
    cookie: '', 
    querySelector: function() {}, 
    createElement: function() { 
     return { 
      pathname: '', 
      setAttribute: function() {} 
     }; 
    } 
}; 

Alcune caratteristiche, ovviamente, non funzioneranno, attacchi alla ecc DOM Ma il quadro di iniezione e, ad esempio il servizio http $ funzionano bene, che è probabilmente quello che vogliamo in un operaio. Ciò che otteniamo con questo è che possiamo eseguire servizi angolari standard in un lavoratore. Pertanto, possiamo testare unitamente i servizi utilizzati nel lavoratore esattamente come faremmo con qualsiasi altra dipendenza angolare.

ho fatto un post che elabora un po 'di più su questo here e ha creato un repo github che crea un servizio che implementa le idee discusse sopra here

+0

Per angolare> = v1.5.7, pensa di aggiungere self.Node = {prototype: []}; vedi https: // github.it/FredrikSandell/angular-workers/issues/15 – Adavo

6

angolare Web Worker con l'esempio di polling

Quando si ha a che fare con i lavoratori in AngularJS è spesso richiesto che il proprio script di lavoro sia in linea (nel caso si utilizzino alcuni strumenti di costruzione come gulp/grunt) e possiamo ottenere questo utilizzando il seguente approccio.

esempio seguente mostra anche come polling può essere fatto per server utilizzando lavoratori:

primo luogo permette di creare la nostra fabbrica lavoratore:

module.factory("myWorker", function($q) { 
    var worker = undefined; 
    return { 
     startWork: function(postData) { 
      var defer = $q.defer(); 
      if (worker) { 
       worker.terminate(); 
      } 

      // function to be your worker 
      function workerFunction() { 
       var self = this; 
       self.onmessage = function(event) { 
        var timeoutPromise = undefined; 
        var dataUrl = event.data.dataUrl; 
        var pollingInterval = event.data.pollingInterval; 
        if (dataUrl) { 
         if (timeoutPromise) { 
          setTimeout.cancel(timeoutPromise); // cancelling previous promises 
         } 

         console.log('Notifications - Data URL: ' + dataUrl); 
         //get Notification count 
         var delay = 5000; // poller 5sec delay 
         (function pollerFunc() { 
          timeoutPromise = setTimeout(function() { 
           var xmlhttp = new XMLHttpRequest(); 
           xmlhttp.onreadystatechange = function() { 
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 
             var response = JSON.parse(xmlhttp.responseText); 
             self.postMessage(response.id); 
             pollerFunc(); 
            } 
           }; 
           xmlhttp.open('GET', dataUrl, true); 
           xmlhttp.send(); 
          }, delay); 
         })(); 
        } 
       } 
      } 
      // end worker function 

      var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string 
      var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds user strict to any function which was blocking might block worker execution so knock it off 

      var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, { 
       type: 'application/javascript; charset=utf-8' 
      }); 

      worker = new Worker(blobURL); 
      worker.onmessage = function(e) { 
       console.log('Worker said: ', e.data); 
       defer.notify(e.data); 
      }; 
      worker.postMessage(postData); // Send data to our worker. 
      return defer.promise; 
     }, 
     stopWork: function() { 
      if (worker) { 
       worker.terminate(); 
      } 
     } 
    } 
}); 

successivo dalla nostra chiamata di controllo della fabbrica operaio:

var inputToWorker = { 
    dataUrl: "http://jsonplaceholder.typicode.com/posts/1", // url to poll 
    pollingInterval: 5 // interval 
}; 

myWorker.startWork(inputToWorker).then(function(response) { 
    // complete 
}, function(error) { 
    // error 
}, function(response) { 
    // notify (here you receive intermittent responses from worker) 
    console.log("Notification worker RESPONSE: " + response); 
}); 

È possibile chiamare myWorker.stopWork(); in qualsiasi momento per terminare il lavoratore dal tuo controller!

Questo viene testato in IE11 + e FF e Chrome

+0

@ChanuSukamo questo non viene richiamato. E tu hai perso il pollingInterval che non ha usato da nessuna parte. – IamStalker

1

si può anche dare un'occhiata a plug angolare https://github.com/vkiryukhin/ng-vkthread

che permette di eseguire una funzione in un thread separato. uso di base:

/* function to execute in a thread */ 
function foo(n, m){ 
    return n + m; 
} 

/* create an object, which you pass to vkThread as an argument*/ 
var param = { 
     fn: foo  // <-- function to execute 
     args: [1, 2] // <-- arguments for this function 
    }; 

/* run thread */ 
vkThread.exec(param).then(
    function (data) { 
     console.log(data); // <-- thread returns 3 
    } 
); 

Esempi e API doc: http://www.eslinstructor.net/ng-vkthread/demo/

--Vadim