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/

- Вадим