Как привязать угловую область к результату веб-работника, использующего интервал?

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

Вот мой worker.js

var timer = false;
var onmessage = function (e) {
    var value = e.data[0];
    var stock = e.data[1];
    var interval = e.data[2];

    if (timer) {
        clearInterval(timer);
    }

    timer = setInterval(function () {
        stock += value;
        postMessage(stock);
    }, interval)
}

Я знаю, как получить результат от веб-работников, использующих обещание, но, как я и ожидал, он работает только для первого обратного вызова.

app.factory('workerServices', ['$q', function ($q) {
    var worker = new Worker('worker.js'),
        defer = $q.defer();

    worker.onmessage = function (e) {
        console.log('Result worker:' + e.data); //displayed each "interval"
        defer.resolve(e.data);
    }

    return {
        increment: function (value, stock, interval) {
            defer = $q.defer();
            worker.postMessage([value, stock, interval]);
            return defer.promise;
        }
    };
}]);

app.controller('mainCtrl', ['$scope', 'workerServices', function ($scope, workerServices) {
    var value = 1,
        stock = 0,
        interval = 300;

    workServices.increment(val, stock, interval).then(function (newStock) {
        $scope.stock = newStock; //updated once

    });
}]);

Поэтому я попытался связать результат с областью действия напрямую, чтобы посмотреть, работает ли она (что мне не понравилось, поскольку я хотел использовать фабрики для своих веб-работников).

И это не сработало. Я не знаю почему, но мой объем остается прежним, даже если console.log работает правильно.

app.controller('mainCtrl', function ($scope) {
    var value = 1,
        stock = 0,
        interval = 300;

    var worker = new Worker('worker.js');
    worker.postMessage([value, stock, interval]);
    worker.onmessage = function (e) {
        console.log(e.data) //properly displayed
        $scope.result = e.data; // never updated
    };
});

Что мне здесь не хватает? Как я могу "постоянно" обновлять переменную из моей области до результатов веб-работника?

2 ответа

Решение

Событие веб-работника onmessage происходит вне углового контекста, поэтому система углового дайджеста не имеет представления об обновлении привязок. В таком случае нам нужно явно сказать угловой, чтобы вручную запустить цикл дайджеста, вызвав $scope.$apply(),

Скорее скажу скорее использовать $timeout что было бы самым безопасным способом применить цикл дайджеста. потому что когда-то напрямую работает $apply метод может вызвать digest cycle is in already progress ошибка из-за конфликта с текущим рабочим циклом.

worker.onmessage = function (e) {
    console.log(e.data) //properly displayed
    $timeout(function(){
       $scope.result = e.data; // never updated
    });
};

Во втором случае, не могли бы вы попробовать $scope().$apply() после $scope.result = data?

И в первом случае, AFAIK, вы не можете выполнить одно и то же обещание дважды, и по этой причине вы не получаете ожидаемого результата. Я бы посоветовал вам воспользоваться шаблоном подписчик-слушатель для достижения этих методов $broadcast/$emit. Этот урок даст вам хорошее начало.

Другие вопросы по тегам