AngularJS и веб-работники
Как можно использовать веб-работников для работы процессов в фоновом режиме? Есть ли какой-нибудь образец, который я должен соблюдать при этом?
В настоящее время я использую сервис, который имеет модель в отдельном веб-рабочем. Эта служба реализует такие методы, как:
ClientsFacade.calculateDebt(client1); //Just an example..
В реализации этот метод отправляет сообщение работнику с данными. Это позволяет мне абстрагироваться от факта, что он выполняется в отдельном потоке, и я мог бы также предоставить реализацию, которая запрашивает сервер или даже тот, который выполняет это действие в том же потоке.
Так как я новичок в javascript, и я просто перерабатываю знания, которые у меня есть с других платформ, мне интересно, это то, что вы бы сделали, или, возможно, Angular, что я использую, предлагает своего рода способ сделать это. Также это вносит изменения в мою архитектуру, так как работник должен явно вводить изменения в контроллер, который затем обновляет свои значения, а затем это отражается в представлении, могу ли я это сделать? Это немного разочаровывает, что веб-работники "защищают" меня так сильно от прикручивания, не позволяя мне делиться памятью и т.д.
Ответы
Ответ 1
Общение с веб-работниками происходит через механизм обмена сообщениями. Перехват этих сообщений происходит при обратном вызове. В AngularJS лучшее место для размещения веб-рабочего - это услуга, которую вы должным образом заметили. Лучший способ справиться с этим - использовать promises, с которым Angular работает удивительно.
Вот пример 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;
}
};
});
Теперь любой внешний объект, который обращается к службе Hello World, не должен заботиться о деталях реализации HelloWorldService
- HelloWorldService
, возможно, обработать данные по web worker
, поверх http
или выполнить обработку прямо там.
Надеюсь, это имеет смысл.
Ответ 2
Очень интересный вопрос! Я нахожу спецификацию веб-работника немного неудобной (вероятно, по уважительным причинам, но все еще неловкой). Необходимость держать рабочий код в отдельном файле делает работу с сервисом трудной для чтения и представляет зависимости от статических URL-адресов файлов в вашем коде приложения angular. Эту проблему можно смягчить, используя 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 и т.д. Но это также означает, что у вас не будет доступа к вашим услугам angular у рабочего без каких-либо возиться. Работник испытывает недостаток в некоторых вещах, которые мы (и angular) ожидаем при выполнении JavaScript в браузере, таких как глобальная переменная "document" и т.д. "Издеваясь над этими требуемыми функциями браузера в рабочем процессе мы можем получить angular для запуска,
var window = self;
self.history = {};
var document = {
readyState: 'complete',
cookie: '',
querySelector: function () {},
createElement: function () {
return {
pathname: '',
setAttribute: function () {}
};
}
};
Некоторые функции, очевидно, не будут работать, привязки к DOM и т.д. Но инфраструктура инъекции и, например, служба $http будут работать очень хорошо, что, вероятно, является тем, что мы хотим в рабочем месте. Что мы получаем от этого, так это то, что мы можем запускать стандартные службы angular у рабочего. Поэтому мы можем использовать unit test службы, используемые в рабочем месте, как и для любой другой зависимости angular.
Я сделал сообщение, в котором подробно описывается здесь и создается репозиторий github, который создает службу, которая реализует идеи, описанные выше здесь
Ответ 3
Я нашел полностью рабочий пример веб-работников в Angular здесь
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;
});
}]);
Он использует promises, чтобы ждать, пока работник вернет результат.
Ответ 4
Angular Веб-рабочий с примером опроса
Когда вы работаете с рабочими в AngularJS, часто требуется, чтобы ваш рабочий script был встроенным (если вы используете некоторые инструменты построения, такие как gulp/grunt), и мы можем достичь этого, используя следующий подход.
Пример ниже также показывает, как опрос может выполняться на сервере с использованием рабочих:
Сначала создадим рабочего factory:
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();
}
}
}
});
Далее от нашего контроллера вызовите рабочего factory:
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
Ответ 5
вы также можете посмотреть плагин angular 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 doc: http://www.eslinstructor.net/ng-vkthread/demo/
- Вадим