2016-10-03 6 views
3

私はサーバーから取得したデータの約束を返すサービスを提供しています。このデータは、表示を最新の状態に保つために一定の間隔で複数回要求されます。応答は時には非常に遅く(最大10秒)、2つのリクエストが重複し、最初のリクエストが最後に応答すると、アプリに表示される情報が古くなってしまいます。タイムラインのようなものになります:約束の返信サービスからの最新データの取得を繰り返し呼び出す

- first request 
Req ---------------------> Res 
- second request 
     Req -------> Res 

現在、私は、要求のカウンタを維持し、それ以上の.then機能を近づけると、それは古すぎる場合は、データを破棄します。私は、すでに約束しているライブラリの1つがそれをしているのか、それを行う標準的な方法があるのだろうかと思います。

また、返されたオブジェクトに応答タイムスタンプを追加する方法や、何らかの形でRxJsのようなものを使用する方法についても説明しましたが、何らかの形で適用されるかどうかはわかりません。

+1

リクエストが完了した順序を決めることは決してできません(実際にサーバー側で実装するのは当然です)。タイムスタンプの考え方がおそらく最良の解決策です。 – Cine

+1

レスポンスタイムスタンプが役立つかどうかわからない...あなたの例では、タイムスタンプが応答にいつ追加されるかに応じて、最初のリクエストで後のタイムスタンプを取得しないでしょうか?またはタイムスタンプはリクエストの時刻です –

+0

@ JaromandaXありがとう、私はそれに気づいていなかった...しかし、私は後で要求が実際に新しい情報を取得することをテストした後、要求時間を代わりに使用することができます:S –

答えて

1

TL & DR:私たちはキャンセル可能な約束をここに発明しています。

まあいいです。インフラストラクチャこれはあなたが本当に必要とする典型的な例ですPromise.cancel()しかし、私たちはES6ネイティブ約束にそれを持っていません。図書館に頼らない人であることから、私はプロミスのサブクラス化によって進化していきます。

次の関数は、約束を取り、それはまたそれに.then().cancel()メソッドを追加__cancelled__と呼ばれる非可算・非設定可能なプロパティを追加することによって、それが解約になりPromise.prototypeを変更することなく、プロパティチェーンです。キャンセル可能な約束オブジェクトのプロトタイプのプロトタイプはPromise.prototypeであるため、キャンセル可能な約束はすべての約束事にアクセスできます。ああ、忘れる前に。キャンセル可能なプロトタイプのthenメソッドもキャンセル可能な約束を返します。

function makePromiseCancellable(p){ 
    Object.defineProperty(p,"__cancelled__", {  value: false, 
               writable: true, 
               enumerable: false, 
              configurable: false 
              }); 
    Object.setPrototypeOf(p,makePromiseCancellable.prototype); 
    return p; 
} 

makePromiseCancellable.prototype = Object.create(Promise.prototype); 
makePromiseCancellable.prototype.then = function(callback){ 
              return makePromiseCancellable(Promise.prototype.then.call(this,function(v){ 
                              !this.__cancelled__ && callback(v); 
                              }.bind(this))); 
              }; 
makePromiseCancellable.prototype.cancel = function(){ 
              this.__cancelled__ = true; 
              return this; 
              }; 

だから私たちは私たちに2000ミリ秒で解決標準ES6の約束を返すgetAsyncData()というユーティリティ機能を持っています。この関数から2つの約束を得て、cp0cp1という取り消し可能約束に変えます。次に、cp0を1000ミリ秒でキャンセルし、何が起こるかを確認します。

function getAsyncData(){ 
 
    var dur = 2000; 
 
    return new Promise((v,x) => setTimeout(v.bind(this,"promise id " + pid++ + " resolved at " + dur + " msec"),dur)); 
 
} 
 

 
function makePromiseCancellable(p){ 
 
    Object.defineProperty(p,"__cancelled__", {  value: false, 
 
               writable: true, 
 
               enumerable: false, 
 
              configurable: false 
 
              }); 
 
    Object.setPrototypeOf(p,makePromiseCancellable.prototype); 
 
    return p; 
 
} 
 

 
makePromiseCancellable.prototype = Object.create(Promise.prototype); 
 
makePromiseCancellable.prototype.then = function(callback){ 
 
              return makePromiseCancellable(Promise.prototype.then.call(this,function(v){ 
 
                              !this.__cancelled__ && callback(v); 
 
                              }.bind(this))); 
 
              }; 
 
makePromiseCancellable.prototype.cancel = function(){ 
 
              this.__cancelled__ = true; 
 
              }; 
 
var pid = 0, 
 
    cp0 = makePromiseCancellable(getAsyncData()); 
 
    cp1 = makePromiseCancellable(getAsyncData()); 
 
cp0.then(v => console.log(v)); 
 
cp1.then(v => console.log(v)); 
 

 
setTimeout(_ => cp0.cancel(),1000);

うわー...!素晴らしい。が2000ミリ秒で解決され、cp0が1000ミリ秒でキャンセルされた。

インフラストラクチャが完成しましたので、これを使用して問題を解決できます。

以下は、私たちが使用するコードです。

function getAsyncData(){ 
    var dur = ~~(Math.random()*9000+1001); 
    return new Promise((v,x) => setTimeout(v.bind(this,"promise id " + pid++ + " resolved at " + dur + " msec"),dur)); 
} 

function runner(fun,cb){ 
    var promises = []; 
    return setInterval(_ => { var prom = makePromiseCancellable(fun()); 
          promises.push(prom); 
          promises[promises.length-1].then(data => { promises.forEach(p => p.cancel()); 
                     promises.length = 0; 
                     return cb(data); 
                    }); 
          },1000); 
} 

var pid = 0, 
    sid = runner(getAsyncData,v => console.log("received data:", v)); 
setTimeout(_=> clearInterval(sid),60001); 

かなり基本的です。 runner()機能が機能しています。それはgetAsyncData()を呼び出すことによって1000msecごとに約束を要求しています。しかし、今度はgetAsyncData()の機能は、1〜10秒で解決できる約束を与えるでしょう。これは、後で約束されたものの一部が以前に受け取ったものの未解決の状態にある間に解決できるようにするためです。あなたの場合のように。 OK;受け取った約束を取り消し可能にした後、runner()関数はそれをpromises配列にプッシュします。約束をpromises配列にプッシュした後でなければ、thenステージから返されたものではなく、主な約束だけを配列に保持したいので、then命令をその配列に追加します。今まで約束していたもののどれかがthenメソッドと呼ばれていた場合、まず配列内のすべての約束をキャンセルしてから配列を空にします。それ以降は、指定されたコールバック関数が呼び出されます。

ここで、すべての動作を見てみましょう。

function makePromiseCancellable(p){ 
 
    Object.defineProperty(p,"__cancelled__", {  value: false, 
 
               writable: true, 
 
               enumerable: false, 
 
              configurable: false 
 
              }); 
 
    Object.setPrototypeOf(p,makePromiseCancellable.prototype); 
 
    return p; 
 
} 
 

 
makePromiseCancellable.prototype = Object.create(Promise.prototype); 
 
makePromiseCancellable.prototype.then = function(callback){ 
 
              return makePromiseCancellable(Promise.prototype.then.call(this,function(v){ 
 
                              !this.__cancelled__ && callback(v); 
 
                              }.bind(this))); 
 
              }; 
 
makePromiseCancellable.prototype.cancel = function(){ 
 
              this.__cancelled__ = true; 
 
              return this; 
 
              }; 
 

 
function getAsyncData(){ 
 
    var dur = ~~(Math.random()*9000+1001); 
 
    return new Promise((v,x) => setTimeout(v.bind(this,"promise id " + pid++ + " resolved at " + dur + " msec"),dur)); 
 
} 
 

 
function runner(fun,cb){ 
 
    var promises = []; 
 
    return setInterval(_ => { var prom = makePromiseCancellable(fun()); 
 
          promises.push(prom); 
 
          promises[promises.length-1].then(data => { promises.forEach(p => p.cancel()); 
 
                     promises.length = 0; 
 
                     return cb(data); 
 
                    }); 
 
          },1000); 
 
} 
 

 
var pid = 0, 
 
    sid = runner(getAsyncData,v => console.log("received data:", v)); 
 
setTimeout(_=> clearInterval(sid),60001);

あなたはそれを停止しない場合runner機能がindefinitelly実行されます。だから60001msecsで私はclearInterval()でそれをクリアします。その期間に約60件の約束が受理され、promises配列で最初に解決された約束の後に受け取った未解決のものを含めて、の前にのすべての約束を取り消すことによって、しかし、後の約束はより新鮮なデータを含むことが予想されるので、それらを解読しないようにしたいかもしれない。次に、コードの次の小さな変更が、最新のデータで頻繁に画面をリフレッシュするという点でより良い結果をもたらすと考えています。もちろん、いくつかの欠点があるかもしれません

function makePromiseCancellable(p){ 
 
    Object.defineProperty(p,"__cancelled__", {  value: false, 
 
               writable: true, 
 
               enumerable: false, 
 
              configurable: false 
 
              }); 
 
    Object.setPrototypeOf(p,makePromiseCancellable.prototype); 
 
    return p; 
 
} 
 

 
makePromiseCancellable.prototype = Object.create(Promise.prototype); 
 
makePromiseCancellable.prototype.then = function(callback){ 
 
              return makePromiseCancellable(Promise.prototype.then.call(this,function(v){ 
 
                              !this.__cancelled__ && callback(v); 
 
                              }.bind(this))); 
 
              }; 
 
makePromiseCancellable.prototype.cancel = function(){ 
 
              this.__cancelled__ = true; 
 
              return this; 
 
              }; 
 

 
function getAsyncData(){ 
 
    var dur = ~~(Math.random()*9000+1001); 
 
    return new Promise((v,x) => setTimeout(v.bind(this,"promise id " + pid++ + " resolved at " + dur + " msec"),dur)); 
 
} 
 

 
function runner(fun,cb){ 
 
    var promises = []; 
 
    return setInterval(_ => { var prom = makePromiseCancellable(fun()); 
 
          promises.push(prom); 
 
          promises[promises.length-1].then(data => { var prix = promises.indexOf(prom); 
 
                     promises.splice(0,prix) 
 
                       .forEach(p => p.cancel()); 
 
                     return cb(data); 
 
                    }); 
 
          },1000); 
 
} 
 

 
var pid = 0, 
 
    sid = runner(getAsyncData,v => console.log("received data:", v)); 
 
setTimeout(_=> clearInterval(sid),60001);

。私はあなたのアイデアを聞きたい。

+0

'then'は2番目のパラメータをとり、最初のパラメータは常に関数であるとは限りません。 – Bergi

+0

@Bergiはいそうです。私は時間があればそれを修正するつもりです。当分の間、それはおそらくOPに考えを与えるでしょう。 – Redu

+0

これは包括的な解決策です。私はそれを何らかのサービスに引き出すことについて見ていきます。 –

関連する問題