2013-05-23 3 views
68

Как можно использовать веб-рабочие, чтобы работать в фоновом режиме? Есть ли какой-нибудь образец, который я должен соблюдать при этом?AngularJS и веб-работники

В настоящее время я пользуюсь службой, у которой есть модель в отдельном веб-работнике. Эта служба реализует методы, такие как:

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

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

Поскольку я новичок в javascript, и я просто перерабатываю знания, которые у меня есть на других платформах. Интересно, будет ли это то, что вы бы сделали или, возможно, Angular, что я использую, предлагает своего рода способ сделать это , Также это вносит изменения в мою архитектуру, так как работник должен явно вводить изменения в контроллер, который затем обновляет свои значения, а затем это отражается в представлении, могу ли я это сделать? Это немного расстраивает то, что веб-работники «защищают» меня так сильно от прикручивания, не позволяя мне делиться памятью и т. Д.

ответ

92

Связь с веб-работниками происходит через механизм обмена сообщениями. Перехват этих сообщений происходит при обратном вызове. В AngularJS лучшее место для размещения веб-рабочего - это услуга, которую вы должным образом заметили. Лучший способ справиться с этим - использовать обещания, с которыми Угловая работает удивительно.

Вот пример webworker в 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; 
     } 
    }; 

}); 

Now любой внешний объект, который обращается к службе Hello World не нужно заботиться о деталях реализации HelloWorldService - HelloWorldService, вероятно, может обрабатывать данные по web worker, более http или выполните обработку прямо там.

Надеюсь, что это имеет смысл.

+3

что ** скрипт doWork.js ** в ** вар работник = новый рабочий ('скрипт doWork.js'); **? – acudars

+2

Это ссылка на внешний файл js, содержащий код для веб-мастера. – ganaraj

+0

Как я могу ссылаться на файл с угловыми маршрутами? Сейчас он должен и асинхронно загружать сценарий с моего сервера. Могу ли я сохранить прямо на клиенте? –

10

я нашел полностью рабочий пример веб-рабочих в угловых 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; 
    }); 

}]); 

Он использует обещания ждать работника вернуть результат.

+3

Сайт, на котором размещен пример, больше не доступен. Вот ссылка на архив https://web.archive.org/web/20150709233911/http://www.codingterminal.com/articles/092014/1/XMLHttpRequest%20using%20AngularJS%20Promises%20and%20HTML5%20Web%20Workers. php –

15

Очень интересный вопрос! Я нахожу спецификацию веб-работника немного неудобной (вероятно, по уважительным причинам, но все еще неловкой). Необходимость сохранения кода работника в отдельном файле делает работу с сервисом трудной для чтения и представляет зависимости от статических URL-адресов файлов в вашем угловом коде приложения. Эту проблему можно смягчить, используя URL.createObjectUrl(), который можно использовать для создания URL-адреса для строки JavaScript. Это позволяет нам указать рабочий код в том же файле, который создает рабочего.

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

Работник спецификации веб также держит рабочий и основной поток контексты совершенно разные, чтобы предотвратить ситуации были тупики и livelocks может произойти и т.д.. Но это также означает, что у вас не будет доступа к вашим угловым услугам у рабочего без какой-либо вопли. Работник испытывает недостаток в некоторых вещах, которые мы (и угловатые) ожидаем при выполнении JavaScript в браузере, таких как глобальная переменная «документ» и т. Д. «Издеваясь над этими требуемыми функциями браузера у работника мы можем получить угловое действие.

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

Некоторые функции, очевидно, не будут работать, привязки к DOM и т.д. Но рамке инъекции и, например, службам HTTP $ будут работать только штраф, который, вероятно, что мы хотим, чтобы в рабочем. Что мы получаем от этого, так это то, что мы можем запускать стандартные угловые сервисы у рабочего. Таким образом, мы можем тестировать службы, используемые в работнике, так же, как и с любой другой угловой зависимостью.

Я сделал пост, который разрабатывает немного больше об этом here и создал GitHub репо, который создает сервис, который реализует идеи обсуждались выше here

+0

Для углового> = v1.5.7 подумайте о добавлении self.Node = {prototype: []}; см. Https: // github.com/FredrikSandell/угловые рабочие/вопросы/15 – Adavo

6

углового Web Worker с примером избирательного

Когда вы имеете дело с рабочими в AngularJS, часто требуется, чтобы ваш рабочий сценарий был встроенным (если вы используете некоторые инструменты построения, такие как gulp/grunt), и мы можем достичь этого, используя следующий подход.

Пример ниже показывает также, как опрос может быть сделано для серверов с помощью работников:

Первый позволяет создать наш работник завода:

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

Следующая из нашего разговора контроллера рабочий на заводе:

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

Вы можете позвонить по телефону myWorker.stopWork(); в любое время, чтобы прекратить работу с вашего контроллера!

Это проверяется в IE11 + и FF и Chrome

+0

@ChanuSukamo это не вызвано. И вы пропустили pollingInterval, он нигде не использовался. – IamStalker

1

вы также можете посмотреть на угловой плагин https://github.com/vkiryukhin/ng-vkthread

, который позволяет выполнить функцию в отдельном потоке. Основного использования:

/* 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 
    } 
); 

Примеры и API документ: http://www.eslinstructor.net/ng-vkthread/demo/

--Vadim