AngularJS та веб-працівники


74

Як angularJS може використовувати веб-працівників для запуску процесів у фоновому режимі? Чи є якийсь зразок, яким я повинен керуватися, роблячи це?

На даний момент я використовую службу, яка має модель в окремому веб-співробітнику. Ця послуга реалізує такі методи, як:

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

У реалізації цей метод надсилає повідомлення працівникові з даними. Це дозволяє мені абстрагувати той факт, що він виконується в окремому потоці, і я також можу надати реалізацію, яка запитує сервер або навіть той, який виконує цю дію в тому ж потоці.

Оскільки я новачок у JavaScript, і я просто переробляю знання, які я маю з інших платформ, мені цікаво, чи це щось ви зробите, чи, можливо, Angular, яким я користуюся, пропонує такий спосіб зробити це. Крім того, це вносить зміни в мою архітектуру, оскільки працівник повинен явно надсилати зміни до контролера, який потім оновлює свої значення, а потім це відображається у поданні, чи я над цим розробляю? Трохи засмучує те, що веб-працівники так сильно "захищають" мене від псування, не дозволяючи ділитися пам'яттю тощо

Відповіді:


98

Спілкування з веб-працівниками відбувається за допомогою механізму обміну повідомленнями. Перехоплення цих повідомлень відбувається під час зворотного дзвінка. У AngularJS найкращим місцем для розміщення веб-працівника є служба, як ви належним чином зазначили. Найкращий спосіб вирішити це - використовувати обіцянки, з якими Angular працює надзвичайно.

Ось приклад a webworkerв aservice

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можливо, він може обробити дані над a web worker, over httpабо виконати обробку прямо там.

Сподіваюся, це має сенс.


5
що таке doWork.js у var worker = new Worker ('doWork.js'); ?
acudars

2
Це посилання на зовнішній файл js, який містить код веб-працівника.
ganaraj

3
так, чи можна використовувати такі послуги, як $ http, у doWork.js?
iLemming

9
Якщо doWorkбуде викликано ще раз до завершення роботи, чи не deferбуде перезаписано? Тоді друга обіцянка вирішиться з першим результатом, а перша обіцянка ніколи не вирішиться.
обіймає

1
@hughes З мого прочитання коду, так, я думаю, ти маєш рацію. Цей код потрібно модифікувати, щоб надати новий відстрочок для вирішення кожного разу, коли викликається doWork (дані).
райман

16

Дуже цікаве питання! Я вважаю специфікацію веб-працівника дещо незграбною (можливо, з поважних причин, але все одно незручною). Потреба зберігати робочий код в окремому файлі ускладнює читання наміру служби та вводить залежності до 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);

Специфікація веб-працівника також повністю підтримує окремі контексти робочого та основного потоків, щоб запобігти ситуаціям, коли можуть виникнути тупикові ситуації, події тощо. Але це також означає, що ви не матимете доступу до своїх кутових послуг у працівника без певної каламуті. Працівнику бракує деяких речей, яких ми (і angular) очікуємо при виконанні JavaScript у браузері, наприклад, глобальна змінна "документ" тощо. "Знущаючись" над необхідними функціями браузера в робочому, ми можемо запустити angular.

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

Деякі функції, очевидно, не працюватимуть, прив'язка до DOM і т. Д. Але ін'єкційний фреймворк і, наприклад, служба $ http, працюватимуть чудово, що, мабуть, і хочеться від працівника. Завдяки цьому ми отримуємо те, що ми можемо запускати стандартні кутові послуги у працівника. Тому ми можемо протестувати модульні послуги, що використовуються у працівника, так само, як і з будь-якою іншою кутовою залежністю.

Я зробив допис, який дещо детальніше про це розповідає тут, і створив репозиторій github, який створює службу, що реалізує ідеї, обговорені вище тут


Для angular> = v1.5.7, спробуйте додати self.Node = {prototype: []}; див. github.com/FredrikSandell/angular-workers/issues/15
Адаво

11

Я знайшов повністю робочий приклад веб - працівників в кутовому тут

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
Сайт, на якому розміщено приклад, більше не доступний. Ось посилання на архів web.archive.org/web/20150709233911/http://…
Michael Khalili

8

Кутовий веб-працівник із прикладом опитування

Коли ви маєте справу з працівниками в 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


@ChanuSukamo це не викликається. І ви пропустили інтервал опитування, який він ніде не використовував.
IamStalker

2

Ви також можете поглянути на кутовий плагін 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/

--Вадим


Це круто!!
MattE

@vadimk Я отримую цю помилку при використанні вашого плагіна! vkThread не є функцією
DevLoverUmar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.