我是RxJS的新手,我正在尝试编写一个可以完成以下任务的应用程序:
fetchItems()
)以获取项目列表。我的first attempt非常直接,达到了目标1,2和4。
var data$ = Rx.Observable.interval(1000)
.startWith('run right away')
.map(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`, but
// I am not yet tracking the `modifiedSince` timestamp yet so all items will always be returned
return fetchItems();
});
现在我很兴奋,这很容易,要达到目标3要困难得多......几个小时之后这就是where I am at:
var modifiedSince = null;
var data$ = Rx.Observable.interval(1000)
.startWith('run right away')
.flatMap(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`
return fetchItems(modifiedSince);
})
.do(function(item) {
if(item.updatedAt > modifiedSince) {
modifiedSince = item.updatedAt;
}
})
.scan(function(previous, current) {
previous.push(current);
return previous;
}, []);
这解决了目标3,但在目标4上倒退。我现在将状态存储在可观察目标之外。
我假设全局modifiedSince
和.do()
块不是实现此目标的最佳方式。任何指导都将不胜感激。
编辑:希望通过这个问题澄清我在寻找什么。
答案 0 :(得分:3)
这是另一个不使用闭包或“外部状态”的解决方案。
我做了以下假设:
fetchItems
会返回Rx.Observable
个项目,即不是项目数组它使用expand
运算符,它允许发出遵循类型x_n+1 = f(x_n)
的递归关系的值。您通过返回发出该值的observable来传递x_n+1
,例如Rx.Observable.return(x_n+1)
,您可以通过返回Rx.Observable.empty()
来完成递归。这似乎你没有结束条件所以这将永远运行。
scan
还允许在递归关系(x_n+1 = f(x_n, y_n)
)之后发出值。区别在于scan
强制您使用同步函数(因此x_n+1
与y_n
同步),而使用expand
则可以使用异步函数一个可观察的。
代码未经过测试,如果有效,请及时通知我。
相关文档:expand,combineLatest
var modifiedSinceInitValue = // put your date here
var polling_frequency = // put your value here
var initial_state = {modifiedSince: modifiedSinceInitValue, itemArray : []}
function max(property) {
return function (acc, current) {
acc = current[property] > acc ? current[property] : acc;
}
}
var data$ = Rx.Observable.return(initial_state)
.expand (function(state){
return fetchItem(state.modifiedSince)
.toArray()
.combineLatest(Rx.Observable.interval(polling_frequency).take(1),
function (itemArray, _) {
return {
modifiedSince : itemArray.reduce(max('updatedAt'), modifiedSinceInitValue),
itemArray : itemArray
}
}
})
答案 1 :(得分:1)
这个怎么样:
var interval = 1000;
function fetchItems() {
return items;
}
var data$ = Rx.Observable.interval(interval)
.map(function() { return fetchItems(); })
.filter(function(x) {return x.lastModified > Date.now() - interval}
.skip(1)
.startWith(fetchItems());
那应该只为新项目过滤来源,再加上完整的集合。只需编写适合您的数据源的过滤器功能。
或者通过将参数传递给fetchItems:
var interval = 1000;
function fetchItems(modifiedSince) {
var retVal = modifiedSince ? items.filter( function(x) {return x.lastModified > modifiedSince}) : items
return retVal;
}
var data$ = Rx.Observable.interval(interval)
.map(function() { return fetchItems(Date.now() - interval); })
.skip(1)
.startWith(fetchItems());
答案 2 :(得分:1)
您似乎意味着modifiedSince
是您所携带的州的一部分,因此它应该出现在scan
中。为什么不将do
中的操作移动到扫描中呢?那么你的种子就是{modifiedSince: null, itemArray: []}
。
错误,我只是认为这可能不起作用,因为您需要将modifiedSince
反馈给上游的fetchItem
函数。你不在这里骑自行车吗?这意味着你必须使用一个主题来打破这个循环。或者,您可以尝试将modifiedSince
封装在闭包中。像
function pollItems (fetchItems, polling_frequency) {
var modifiedSince = null;
var data$ = Rx.Observable.interval(polling_frequency)
.startWith('run right away')
.flatMap(function() {
// `fetchItems(modifiedSince)` returns an array of items modified after `modifiedSince`
return fetchItems(modifiedSince);
})
.do(function(item) {
if(item.updatedAt > modifiedSince) {
modifiedSince = item.updatedAt;
}
})
.scan(function(previous, current) {
previous.push(current);
return previous;
}, []);
return data$;
}
我必须跑出去庆祝新的一年,如果这不起作用,我可以稍后再尝试(可能使用expand
运算符,scan
的其他版本。)