AngularJS和网络工作者

时间:2013-05-23 12:26:57

标签: javascript angularjs web-worker

angularJS如何使用Web worker在后台运行进程?这样做我应该遵循什么模式?

目前,我正在使用一个在单独的Web worker中具有该模型的服务。该服务实现了以下方法:

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

在实现中,此方法使用数据向worker发送消息。这允许我抽象它在一个单独的线程中执行的事实,我还可以提供一个查询服务器的实现,甚至是在同一个线程中执行此操作的实现。

由于我是javascript的新手,我只是回收其他平台上的知识,我想知道这是你要做的事情,还是我正在使用的Angular,提供了一种方法。这也引入了我的体系结构的变化,因为工作者必须明确地将更改推送到控制器,然后控制器更新其值,然后这反映在视图中,我是否过度设计了这个?有点令人沮丧的是,网络工作者通过不允许我共享内存等来“保护”我这么多。

5 个答案:

答案 0 :(得分:96)

通过消息传递机制与Web工作者进行通信。拦截这些消息发生在回叫中。在AngularJS中,正如您所注意到的那样,放置Web工​​作者的最佳位置是在服务中。解决这个问题的最好方法是使用Promular,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处理数据或者在那里进行处理。

希望这是有道理的。

答案 1 :(得分:16)

一个非常有趣的问题!我发现web worker规范有点尴尬(可能有充分的理由,但仍然很尴尬)。将工作程序代码保存在单独的文件中的需要使得服务的意图难以阅读,并且在角度应用程序代码中引入了对静态文件URL的依赖性。使用URL.createObjectUrl()可以缓解此问题,该URL可用于为JavaScript字符串创建URL。这允许我们在创建worker的同一文件中指定worker代码。

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

Web worker规范还使工作者和主线程上下文完全分开,以防止出现死锁和活锁等情况。但这也意味着你无法在工作人员中获得角度服务,而无需进行一些调整。在浏览器中执行JavaScript时,工作者缺少我们(和角度)期望的一些东西,比如全局变量" document"通过"嘲笑"这些工作者需要的浏览器功能,我们可以运行角度。

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

某些功能显然不会起作用,绑定到DOM等。但是注入框架和例如$ http服务可以正常工作,这可能是我们想要的工作者。我们得到的是我们可以在工人中运行标准角度服务。因此,我们可以像对待任何其他角度依赖一样对工作中使用的服务进行单元测试。

我发了一篇文章详细阐述了这个here并创建了一个github repo,它创建了一个服务来实现上面讨论的想法here

答案 2 :(得分:11)

我在Angular here

中找到了一个完整的Web工作者示例
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等待worker返回结果。

答案 3 :(得分:7)

  

带有投票示例的Angular Web Worker

当您与AngularJS中的工作人员打交道时,通常要求您的工作脚本是内联的(如果您使用的是一些构建工具,如gulp / grunt),我们可以使用以下方法实现此目的。

下面的示例还说明了如何使用worker对服务器进行轮询:

首先让我们创建我们的工厂:

    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

中测试的

答案 4 :(得分: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/

- 瓦迪姆