首页 文章

AngularJS和网络工作者

提问于
浏览
70

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

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

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

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

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

5 回答

  • 11

    与Web worker的通信通过消息传递机制实现 . 拦截这些消息发生在回叫中 . 在AngularJS中,正如您所注意到的那样,放置Web工作者的最佳位置是在服务中 . 解决这个问题的最好方法是使用Promular,Angular可以使用它 .

    以下是 servicewebworker 的示例

    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 或者在那里进行处理 .

    希望这是有道理的 .

  • 95

    一个非常有趣的问题!我发现Web工作者规范有点尴尬(可能有充分的理由,但仍然很尴尬) . 将工作程序代码保存在单独的文件中的需要使得服务的意图难以阅读,并且在角度应用程序代码中引入了对静态文件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规范还使工作者和主线程上下文完全分开,以防止出现死锁和活锁等情况 . 但这也意味着如果没有一些摆弄,你将无法在 Worker 中获得角度服务 . 在浏览器中执行JavaScript时,工作者缺少我们(和角度)期望的一些东西,比如全局变量“document”等 . 通过“模拟”工作者中所需的浏览器功能,我们可以获得角度运行 .

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

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

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

  • 2

    我在Angular中找到了一个完整的Web工作者示例here

    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返回结果 .

  • 16

    带有轮询示例的Angular Web Worker

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

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

    First lets create our worker 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();
                }
            }
        }
    });
    

    Next from our controller call the worker 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中测试的

  • 7

    你也可以看一下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文档:http://www.eslinstructor.net/ng-vkthread/demo/

    --Vadim

相关问题