有一个observable返回数组/事物列表:Observable
我有一个用例,对于这个observable的下游消费者而言,这个列表中添加了更多项目是非常昂贵的事情。所以我想减慢对此列表所做的添加量,但不要放松。
类似于一个运算符,它接受这个observable并返回另一个具有相同签名的observable,但每当一个新列表被推送到它并且它有比上次更多的项时,那么一次只添加一个或几个。
因此,如果最后一次推送是一个包含3个项目的列表,而下一个推送是6个项目,批次大小为1,那么这一个列表推送将分成10个单独的列表大小:4,5,6
因此添加是批量的,这样消费者可以更容易地跟上列表的新增内容。或者消费者在处理数组/列表中的其他项目时,每次都不必停顿这么长时间,因为添加内容会被拆分并分布在可配置的批量大小上。
答案 0 :(得分:0)
我创建了一个addAdditionalOnIdle运算符,您可以使用let运算符将其应用于任何rxjs observable。它需要一个batchSize参数,因此您可以配置批量大小。它还需要一个dontBatchAfterThreshold,它会在某个列表大小之后停止对列表进行批处理,这对我的目的很有用。
该实现在内部使用新的requestIdleCallback函数来在浏览器中有空闲时间时调度其他项目的批量推送。这个功能在IE或Safari中还没有,但是我发现这个功能非常好,所以今天你可以使用它:https://github.com/aFarkas/requestIdleCallback:)
请参阅下面的addAdditionalOnIdle的实现和示例用法:
function addAdditionalOnIdle(
batchSize = 1,
dontBatchAfterThreshold = 22,
) {
return (source) => {
let idleCallback;
let currentPushedItems = [];
let lastItemsReceived = [];
return Rx.Observable.create((observer) => {
let sourceSubscription = source
.subscribe({
complete: () => {
observer.complete();
},
error: (error) => {
observer.error(error);
},
next: (items) => {
try {
lastItemsReceived = items;
if (idleCallback) {
return;
}
if (items.length > currentPushedItems.length) {
const idleCbFn = () => {
if (currentPushedItems.length > lastItemsReceived.length) {
throw new Error('currentPushedItems should never be larger than lastItemsReceived.');
}
const from = currentPushedItems.length;
const to = from + batchSize;
const last = lastItemsReceived.length;
if (from < dontBatchAfterThreshold) {
for (let i = from ; i < to && i < last ; i++ ) {
currentPushedItems[i] = lastItemsReceived[i];
}
} else {
currentPushedItems = lastItemsReceived;
}
if (currentPushedItems.length < lastItemsReceived.length) {
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
idleCallback = undefined;
}
observer.next(currentPushedItems);
};
idleCallback = window.requestIdleCallback(() => {
idleCbFn();
});
} else {
currentPushedItems = items;
observer.next(currentPushedItems);
}
} catch (error) {
observer.error(error);
}
},
});
return () => {
sourceSubscription.unsubscribe();
sourceSubscription = undefined;
lastItemsReceived = undefined;
currentPushedItems = undefined;
if (idleCallback) {
window.cancelIdleCallback(idleCallback);
idleCallback = undefined;
}
observer = undefined;
};
});
};
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
let testSource = Rx.Observable.of(
[1,2,3],
[1,2,3,4,5,6],
)
.concat(Rx.Observable.never());
testSource
.let(addAdditionalOnIdle(2))
.subscribe((list) => {
// Simulate a slow synchronous consumer with a busy loop sleep implementation
sleep(1000);
document.body.innerHTML += "<p>" + list + "</p>";
});
&#13;
<script src="https://unpkg.com/rxjs@5.4.3/bundles/Rx.min.js"></script>
&#13;