75

angularJS はどのように Web ワーカーを使用してバックグラウンドでプロセスを実行できますか? これを行う際に従うべきパターンはありますか?

現在、別の Web ワーカーにモデルがあるサービスを使用しています。このサービスは、次のようなメソッドを実装します。

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

実装では、このメソッドはワーカーにデータとともにメッセージを送信します。これにより、別のスレッドで実行されているという事実を抽象化でき、サーバーに対してクエリを実行する実装、または同じスレッドでこのアクションを実行する実装を提供することもできます。

私はjavascriptが初めてで、他のプラットフォームから持っている知識をリサイクルしているだけなので、これはあなたがすることなのか、それとも私が使用しているAngularがこれを行う一種の方法を提供するのだろうか. また、ワーカーがコントローラーに変更を明示的にプッシュする必要があるため、アーキテクチャに変更が生じます。コントローラーはその値を更新し、これがビューに反映されます。これはオーバーエンジニアリングですか? メモリの共有などを許可しないことで、Web ワーカーが私を台無しにしないように「保護」するのは少しイライラします。

4

5 に答える 5

99

Web ワーカーとの通信は、メッセージング メカニズムを通じて行われます。これらのメッセージのインターセプトは、コールバックで発生します。AngularJS では、Web ワーカーを配置するのに最適な場所は、あなたが正式に指摘したようにサービスです。これに対処する最善の方法は、Angular が驚くほどうまく機能する promise を使用することです。

webworkera のaの例を次に示します。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 サービスにアクセスする外部エンティティは、実装の詳細を気にする必要はありませんHelloWorldServiceHelloWorldServiceおそらく、 、 、またはその場でデータを処理することができweb workerますhttp

これが理にかなっていることを願っています。

于 2013-05-24T08:38:23.373 に答える
17

とても興味深い質問です!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 ワーカーの仕様では、デッドロックやライブロックなどの状況が発生するのを防ぐために、ワーカー コンテキストとメイン スレッド コンテキストを完全に分離しています。ただし、これは、なんらかの操作を行わないと、ワーカー内の角度付きサービスにアクセスできないことも意味します。ワーカーには、ブラウザーで JavaScript を実行する際に期待する (および角度のある) ものがいくつかありません。たとえば、グローバル変数 "document" などです。これらの必要なブラウザー機能をワーカーで "モック" することで、Angular を実行することができます。

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

DOM へのバインドなど、一部の機能は明らかに動作しません。しかし、インジェクション フレームワークと、たとえば $http サービスは問題なく動作します。これはおそらく、ワーカーに必要なものです。これにより得られるのは、ワーカーで標準の角度サービスを実行できることです。したがって、他の角度依存関係と同じように、ワーカーで使用されるサービスを単体テストできます。

ここでこれについてもう少し詳しく説明する投稿を作成し、ここで上で説明したアイデアを実装するサービスを作成する github リポジトリを作成しました

于 2015-02-17T20:44:11.023 に答える
11

ここで、Angular での 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 を使用して、ワーカーが結果を返すのを待ちます。

于 2014-10-31T12:22:53.883 に答える
8

ポーリングの例を使用した 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 でテストされています

于 2016-05-11T08:06:00.890 に答える