简体   繁体   English

从重复调用的promise返回服务中获取最新数据

[英]Getting the latest data from a promise returning service called repeatedly

I have an angular service that returns a promise for data obtained from a server. 我有一个角度服务,它返回从服务器获取的数据的承诺。 This data is requested multiple times in a fixed interval to keep the display up to date. 在固定间隔内多次请求此数据以使显示保持最新。 The response can sometimes be quite slow (up to 10 seconds), and if two requests overlap and the first one responds last I'll get out of date information displayed in the app. 响应有时可能非常慢(最多10秒),如果两个请求重叠且第一个请求最后响应,我将得到应用程序中显示的过期信息。 The timelines would be something like: 时间表将是这样的:

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

Currently I keep a counter of the request and make the .then function close over it and discard the data if it is too old. 目前我保留了一个请求的计数器,并使.then函数关闭它并丢弃数据,如果它太旧了。 I wonder if one of the promise libraries out there already does it or if there is a standard way to do it. 我想知道其中一个承诺库是否已经存在,或者是否有标准的方法来实现它。

I've also though about adding the response timestamp to the returned object, or somehow using something like RxJs but I haven't used it to know if it applies somehow. 我还想将响应时间戳添加到返回的对象,或者以某种方式使用类似RxJs的东西,但我还没有用它来知道它是否以某种方式应用。

TL&DR: We are inventing cancellable promises here. TL&DR:我们在这里发明了可取消的承诺。

Well.. OK. 嗯..好的。 Some infrastructure. 一些基础设施 This is a typical example where you really need Promise.cancel() However we don't have it in ES6 native promises. 这是一个典型的例子,你真的需要Promise.cancel()但是我们在ES6原生承诺中没有它。 Being a library agnostic person i just go ahead and invent one by Promise sub-classing. 作为一个与图书馆无关的人我只是继续发明一个由Promise子分类。

The following function takes a promise and makes it cancellable by adding a non-enumerable and non-configurable property called __cancelled__ It also adds .then() and .cancel() methods to it's property chain without modifying the Promise.prototype . 以下函数接受一个promise并通过添加一个名为__cancelled__的非可枚举且不可配置的属性__cancelled__它还将__cancelled__ .then().cancel()方法添加到它的属性链而不修改Promise.prototype Since cancellable promise object's proptotype's prototype is Promise.prototype , our cancellable promise has access to all Promise thingies. 由于可取消的promise对象的proptotype原型是Promise.prototype ,我们的可取消承诺可以访问所有Promise的东西。 Ah.. before i forget; 啊..在我忘记之前; cancellable prototype's then method also returns a cancellable promise. 可取消的原型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;
                                          };

So we have a utility function called getAsyncData() which returns us a standard ES6 promise which resolves in 2000 msecs. 所以我们有一个名为getAsyncData()的实用程序函数,它返回一个标准的ES6承诺,它可以在2000 getAsyncData()内解析。 We will obtain two promises from this function, and turn them into cancellable promises called cp0 and cp1 . 我们将获得这两个作用的承诺,并把它们变成所谓的撤销承诺cp0cp1 Then we will cancel cp0 at 1000 msecs and see what happens. 然后我们将在1000 cp0取消cp0 ,看看会发生什么。

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

Wow..! 哇..! fantastic. 太棒了。 cp1 resolved at 2000 msec while cp0 has got cancelled at 1000 msecs. cp1在2000毫秒解决,而cp0曾在1000毫秒被取消了。

Now, since we now have the infrastructure, we can use it to solve your problem. 现在,由于我们现在拥有基础设施,我们可以用它来解决您的问题。

The following is the code that we will use; 以下是我们将使用的代码;

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

It's pretty basic. 这很基础。 The runner() function is doing the job. runner()函数正在完成这项工作。 It's requesting a promise every 1000msecs by invoking getAsyncData() . 它通过调用getAsyncData()每1000毫秒请求一个promise。 The getAsyncData() function however this time will give us a promise which will resolve in 1~10 seconds. 但是这次getAsyncData()函数会给我们一个在1~10秒内解析的promise。 This is so because we want some of the later promises to be able to resolve while some of the previously received ones are still in unresolved state. 这是因为我们希望一些后来的承诺能够解决,而一些先前收到的承诺仍处于未解决的状态。 Just like in your case. 就像你的情况一样。 OK; 好; after making the received promise cancellable, the runner() function pushes it into the promises array. 在接收到的promise被取消后, runner()函数将其推送到promises数组中。 Only after pushing the promise to the promises array we attach the then instruction to it because we want the array to hold only the main promises, not the ones returned from the then stage. 只有在将promise发送到promises数组之后,我们才会将then指令附加到它,因为我们希望数组只保存主要的promises,而不是那些从then阶段返回的promises。 Which ever promise resolves first and calls it's then method, will first cancel all the promises in the array and then empty the array; 而且,任何承诺解决第一,并调用它的then方法,将首先取消阵列中的所有承诺,然后清空阵列; only after that will invoke the provided callback function. 只有在那之后才会调用提供的回调函数。

So now let's see the whole thing in action. 所以现在让我们看看整个过程。

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

The runner function will run indefinitelly if you don't stop it. 如果你不停止, runner功能将无限期地运行。 So at 60001msecs I clear it by a clearInterval() . 所以在60001msecs我通过clearInterval()清除它。 During that period 60 promises will be received and only the first resolvers will invoke the provided callback by cancelling all the previous currently received promises, including the still unresolved ones, those received after the first resolving promise in our promises array. 在此期间将收到60个承诺,并且只有第一个解析器将通过取消所有先前当前收到的承诺来调用所提供的回调,包括尚未解决的承诺,即在我们的promises阵列中的第一个解决承诺之后收到的promises However since those later promises are expected to contain more fresh data, one might want to keep them uncancelled. 然而,由于这些后来的承诺预计会包含更多新鲜数据,因此人们可能希望将它们保持不变。 Then I suppose the following small change in the code will do better in terms of refreshing the screen more frequently with the latest data. 然后,我认为代码中的以下小变化在使用最新数据更频繁地刷新屏幕方面会做得更好。

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

There might be some flaws of course. 当然可能存在一些缺陷。 I would like to hear your ideas. 我想听听你的想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM