AngularJS 和网络工作者

AngularJS 如何使用 web worker 在后台运行进程?我这样做有什么模式吗?

目前,我正在使用一个服务,该服务在单独的 Web worker 中使用模型:

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

在实现中,此方法向带有数据的工作者发送消息。这使我能够抽象出这样一个事实,即它是在一个单独的线程中执行的,我还可以提供一个实现来查询服务器,甚至可以提供一个在同一个线程中执行此操作的实现。

因为我是 javascript 的新手,我只是在回收我在其他平台上学到的知识,我想知道这是否是你会做的事情,或者我正在使用的 Angular,提供了一种做这件事的方法。同时这也引入了我的架构的变化,因为工作者必须明确地将变化推送到控制器,然后更新它的值,然后这反映在视图中,我是否过度工程化了?网络工作者不允许我分享内存等等,以此来“保护”我免于把事情搞砸,这有点令人沮丧。

49725 次浏览

与 Web 工作者的通信是通过消息传递机制进行的。拦截这些消息发生在回调过程中。在 AngularJS 中,放置 web worker 的最佳位置是在您适当注意到的服务中。处理这个问题的最好方法就是使用保证,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 workerhttp处理数据,或者就在那里进行处理。

希望这能说得通。

我发现一个完全工作的例子,在角 给你网络工人

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;
});


}]);

它使用承诺等待工人返回结果。

一个非常有趣的问题!我发现 Web worker 规范有点笨拙(可能是出于好的原因,但仍然笨拙)。需要将工作代码保存在一个单独的文件中,这使得服务的意图很难读取,并且将依赖关系引入到您的有角度的应用程序代码中的静态文件 URL 中。这个问题可以通过使用 URL.createObjectUrl ()来缓解,该 URL.createObjectUrl ()可用于为 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 中使用的服务进行单元测试,就像对任何其他角度依赖一样。

我发表了一篇文章,详细阐述了这个 给你和创建了一个 github 回购,其中创建了一个服务,实现了上述 给你讨论的想法

带轮询的角度 Web 工作器示例

当你处理 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();
}
}
}
});

接下来从我们的控制器调用 worker 工厂:

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 中测试的

你也可以看看角插件 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/

瓦迪姆