从重复调用的promise返回服务中获取最新数据 [英] Getting the latest data from a promise returning service called repeatedly

查看:153
本文介绍了从重复调用的promise返回服务中获取最新数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个角度服务,它返回从服务器获取的数据的承诺。在固定间隔内多次请求此数据以使显示保持最新。响应有时可能非常慢(最多10秒),如果两个请求重叠且第一个请求最后响应,我将得到应用程序中显示的过期信息。时间表如下:

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. The timelines would be something like:

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

目前我保留了一个请求计数器并使.then函数关闭它并丢弃数据是否太旧了。我想知道其中一个承诺库是否已经存在,或者是否有标准方法可以执行此操作。

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. I wonder if one of the promise libraries out there already does it or if there is a standard way to do it.

我还要将响应时间戳添加到返回的对象,或以某种方式使用类似RxJs的东西,但我还没有用它来知道它是否以某种方式应用。

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.

推荐答案

TL& DR:我们在这里发明了可取消的承诺。

TL&DR: We are inventing cancellable promises here.

嗯..好的。一些基础设施这是一个典型的例子,你确实需要 Promise.cancel()但是我们在ES6原生承诺中没有它。作为一个与图书馆无关的人我只是继续发明一个由Promise子分类。

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. Being a library agnostic person i just go ahead and invent one by Promise sub-classing.

以下函数接受一个承诺并通过添加一个不可枚举的东西使其可以取消不可配置的属性名为 __ cancel __ 它还添加 .then() .cancel() 其属性链的方法,而不修改 Promise.prototype 。由于可取消的promise对象的proptotype的原型是 Promise.prototype ,我们的可取消承诺可以访问所有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. Since cancellable promise object's proptotype's prototype is Promise.prototype, our cancellable promise has access to all Promise thingies. Ah.. before i forget; cancellable prototype's then method also returns a cancellable promise.

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

所以我们有一个名为的实用程序函数getAsyncData()它返回一个标准的ES6承诺,它在2000 msecs中解决。我们将从此函数中获得两个promise,并将它们转换为名为 cp0 cp1 的可取消承诺。然后我们将在1000毫秒取消 cp0 ,看看会发生什么。

So we have a utility function called getAsyncData() which returns us a standard ES6 promise which resolves in 2000 msecs. We will obtain two promises from this function, and turn them into cancellable promises called cp0 and cp1. Then we will cancel cp0 at 1000 msecs and see what happens.

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

哇..!太棒了。 cp1 在2000毫秒时解决,而 cp0 已取消1000毫秒。

Wow..! fantastic. cp1 resolved at 2000 msec while cp0 has got cancelled at 1000 msecs.

现在,由于我们现在拥有基础设施,我们可以使用它来解决您的问题。

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

这是非常基本的。 runner()函数正在执行此任务。它通过调用 getAsyncData()每1000毫秒请求一个承诺。 getAsyncData()函数但是这次会给我们一个将在1~10秒内解析的承诺。这是因为我们希望一些后来的承诺能够解决,而一些先前收到的承诺仍处于未解决的状态。就像你的情况一样。好;在接收到的承诺可以取消后, runner()函数将其推送到 promises 数组中。只有在将承诺推送到 promises 数组后,我们才会将then指令附加到它,因为我们希望数组只保留主承诺,而不是从<$ c返回的承诺$ c>然后阶段。哪个承诺首先解析并调用它的然后方法,将首先取消数组中的所有promise,然后清空数组;只有在那之后才会调用提供的回调函数。

It's pretty basic. The runner() function is doing the job. It's requesting a promise every 1000msecs by invoking getAsyncData(). The getAsyncData() function however this time will give us a promise which will resolve in 1~10 seconds. 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. 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. 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; 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);

如果不停止, runner 函数将运行indefinitelly。所以在60001msecs我用 clearInterval()清除它。在此期间将收到60个承诺,只有第一个解析器将通过取消所有以前当前收到的承诺来调用所提供的回调,包括尚未解决的承诺,在我们的第一个解决承诺之后收到的承诺code> promises 数组。然而,由于这些后来的承诺预计会包含更多新鲜数据,因此人们可能希望将它们保持不变。然后,我认为代码中的以下小变化在使用最新数据更频繁地刷新屏幕方面会做得更好。

The runner function will run indefinitelly if you don't stop it. So at 60001msecs I clear it by a 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. 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.

这篇关于从重复调用的promise返回服务中获取最新数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆