AngularJSはどのようにWebワーカーを使用してバックグラウンドでプロセスを実行できますか?これを行う上で従うべきパターンはありますか?
現在、私は別のWeb Workerにモデルを持つサービスを使用しています。このサービスは次のようなメソッドを実装します。
ClientsFacade.calculateDebt(client1); //Just an example..
実装では、このメソッドはデータとともにメッセージをワーカーに送信します。これにより、別のスレッドで実行されているという事実を抽象化できます。また、サーバーまたは同じスレッドでこのアクションを実行するサーバーに対してクエリを実行する実装を提供することもできます。
私はjavascriptを初めて使用し、他のプラットフォームから得た知識をリサイクルしているだけなので、これがあなたがするものか、おそらくAngularこれは私が使用しているもので、また、これは、ワーカーがコントローラに変更を明示的にプッシュする必要があるため、アーキテクチャに変更を導入します。これにより、値が更新され、ビューに反映されます。ウェブワーカーは、メモリの共有などを許可しないことで、私を混乱から大いに「保護」します。
Webワーカーとの通信は、メッセージングメカニズムを介して行われます。これらのメッセージの傍受は、コールバックで発生します。 AngularJSでは、Webワーカーを配置するのに最適な場所は、既に述べたようにサービス内です。これに対処する最良の方法は、Angularで驚くほどうまくいくPromiseを使用することです。
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
、またはすぐに処理します。
これが理にかなっていることを願っています。
非常に興味深い質問です! Webワーカーの仕様は少し厄介だと思います(おそらく正当な理由がありますが、やはり厄介です)。ワーカーコードを別のファイルに保存する必要があるため、サービスの意図が読みにくくなり、angularアプリケーションコードの静的ファイルURLへの依存関係が生じます。この問題は、JavaScript文字列のURLを作成するために使用できるURL.createObjectUrl()を使用して軽減できます。これにより、ワーカーを作成する同じファイルにワーカーコードを指定できます。
var blobURL = URL.createObjectURL(new Blob([
"var i = 0;//web worker body"
], { type: 'application/javascript' }));
var worker = new Worker(blobURL);
また、Webワーカーの仕様では、ワーカーとメインスレッドのコンテキストを完全に分離して、デッドロックやライブロックなどが発生する可能性のある状況を防止しています。しかし、それはまた、いじりまわさない限り、ワーカーのangularサービスにアクセスできないことを意味します。ワーカーは、グローバル変数「ドキュメント」など、ブラウザーでJavaScriptを実行するときに期待するもの(および角度)の一部を欠いています。これらの必要なブラウザー機能をワーカーで「モック」することで、angular走る。
var window = self;
self.history = {};
var document = {
readyState: 'complete',
cookie: '',
querySelector: function () {},
createElement: function () {
return {
pathname: '',
setAttribute: function () {}
};
}
};
DOMへのバインドなど、一部の機能は明らかに機能しません。しかし、インジェクションフレームワークと、たとえば$ httpサービスはうまく機能します。これはおそらくワーカーに必要なものです。これによって得られるのは、ワーカーで標準のangularサービスを実行できることです。したがって、他のangular依存関係と同様に、ワーカーで使用されるサービスを単体テストできます。
これについてもう少し詳しく説明する投稿を作成しました here と、上記のアイデアを実装するサービスを作成するgithubリポジトリを作成しました here
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;
});
}]);
Promiseを使用して、ワーカーが結果を返すのを待ちます。
ポーリングの例を使用したAngular Web Worker
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でテストされています
angular plugin 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/
-ヴァディム