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

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

问题描述

我有一个 Angular 服务,它返回对从服务器获取的数据的承诺.以固定间隔多次请求此数据以保持显示最新.响应有时会很慢(最多 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 中没有它.作为一个图书馆不可知论者,我只是继续通过 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.

以下函数接受一个 promise 并通过添加一个名为 __cancelled__ 的不可枚举和不可配置的属性使其可取消,它还添加了 .then()>.cancel() 方法到它的属性链而不修改 Promise.prototype.由于可取消的promise对象的proptotype的原型是Promise.prototype,我们的可取消的promise可以访问所有的Promise对象.啊..在我忘记之前;可取消原型的 then 方法也返回一个可取消承诺.

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 毫秒内解析.我们将从这个函数中获得两个promise,并将它们转化为可取消的promise,称为cp0cp1.然后我们将在 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 数组中.只有在将 promise 推送到 promises 数组之后,我们才将 then 指令附加到它上面,因为我们希望数组只保存主要的 promise,而不是从 then 阶段返回的那些.哪个promise首先解析并调用它的then方法,将首先取消数组中的所有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 函数会无限期运行,如果你不停止它.所以在 60001 毫秒时,我通过 clearInterval() 清除它.在此期间,将收到 60 个承诺,并且只有第一个解析器会通过取消所有 先前 当前收到的承诺来调用提供的回调,包括仍未解决的承诺,即在我们的 中的第一个解决承诺之后收到的承诺代码>承诺数组.然而,由于那些后来的承诺预计会包含更多的新数据,人们可能希望保持它们不被取消.那么我认为代码中的以下小改动会更好地使用最新数据更频繁地刷新屏幕.

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.

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

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