web-dev-qa-db-ja.com

AngularJSサービスでのpromiseオブジェクトのキャッシュ

Promisesを使用して、AngularJSに静的リソースの動的ロードを実装したいと思います。問題:ページにいくつかのコンポーネントがあり、サーバーから静的リソースを取得する必要がある(または表示されないため、動的である)場合があります。ロードされると、アプリケーションの全ライフサイクルにわたってキャッシュできます。

私はこのメカニズムを実装しましたが、Angular and Promisesを初めて使用するので、これが正しいソリューションであるかどうかを確認したい\アプローチ。

var data = null;
var deferredLoadData = null;

function loadDataPromise() {
  if (deferredLoadData !== null)
    return deferredLoadData.promise;

  deferredLoadData = $q.defer();

  $http.get("data.json").then(function (res) {
    data = res.data;
    return deferredLoadData.resolve();
  }, function (res) {
    return deferredLoadData.reject();
  });

  return deferredLoadData.promise;
}

したがって、要求は1つだけ作成され、次にloadDataPromise()を呼び出すと、最初に作成されたpromiseが返されます。進行中のリクエストや、すでに少し前に終了したリクエストに対してはうまくいくようです。

しかし、それはプロミスをキャッシュするための良い解決策ですか?

35
andrew.fox

これは正しいアプローチですか?

はい。返す関数で memoisation を使用すると、非同期(通常は高価な)タスクの繰り返し実行を回避するための一般的な手法が約束されます。進行中の操作と終了した操作を区別する必要がないため、プロミスはキャッシングを簡単にします。どちらも結果値の(同じ)プロミスとして表されます。

これは正しい解決策ですか?

いいえ。そのグローバルdata変数とundefinedによる解決は、promiseの動作を意図したものではありません。代わりに、結果data!また、コーディングが非常に簡単になります。

_var dataPromise = null;

function getData() {
    if (dataPromise == null)
        dataPromise = $http.get("data.json").then(function (res) {
           return res.data;
        });
    return dataPromise;
}
_

次に、loadDataPromise().then(function() { /* use global */ data })ではなく、単にgetData().then(function(data) { … })になります。

パターンをさらに改善するには、クロージャースコープでdataPromiseを非表示にし、getDataがパラメーター(URLなど)を受け取るときに別のPromiseのルックアップが必要になることに注意してください。

48
Bergi

このタスクでは、このすべてのボイラープレートコードを削除するdefer-cache-serviceというサービスを作成しました。 TypeScriptで記述されていますが、コンパイル済みのjsファイルを取得できます。 Github ソースコード

例:

function loadCached() {
   return deferCacheService.getDeferred('cacke.key1', function () {
      return $http.get("data.json");
   }); 
} 

そして消費する

loadCached().then(function(data) {
//...
});

同じloadDataPromiseを同時に呼び出す2つ以上の部分がある場合、このチェックを追加する必要があることに注意してください。

if (defer && defer.promise.$$state.status === 0) {
   return defer.promise;
}

それ以外の場合は、バックエンドへの重複した呼び出しを行います。

3

このデザインデザインパターンはcacheが最初に実行されたときに返されるものをすべて再呼び出しし、毎回cachedを返します。

const asyncTask = (cache => {
  return function(){
    // when called first time, put the promise in the "cache" variable
    if( !cache ){
        cache = new Promise(function(resolve, reject){
            setTimeout(() => {
                resolve('foo');
            }, 2000);
        });
    }
    return cache;
  }
})();

asyncTask().then(console.log);
asyncTask().then(console.log);

説明:

関数(元の非同期関数)を返す別の自己呼び出し関数で関数をラップするだけです。ラッパー関数の目的は、ローカル変数cacheのカプセル化スコープを提供して、ローカル変数にのみアクセスできるようにすることです。ラッパー関数の返された関数内で、asyncTaskが呼び出されるたびに(最初以外は)まったく同じ値を持つ

0
vsync