[英]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
. 我们将获得这两个作用的承诺,并把它们变成所谓的撤销承诺
cp0
和cp1
。 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.