看到這個演示:https://jsbin.com/todude/10/edit?js,console
注意到,我在1200ms
時的情況下是無效的,然後試圖讓緩存結果在1300ms
時,前一個請求仍然未決(需要200ms
)。兩個結果都應該收到。
這是因爲,當您訂閱和publishReplay()
不包含任何有效的數值也不會發出任何東西,也不會立即完成(感謝take(1)
),所以它需要訂閱的源這使得HTTP請求(這實際上發生在refCount()
)。
然後第二個用戶也不會收到任何東西,並將被添加到publishReplay()
的觀察員陣列中。它不會再訂閱,因爲它已經訂閱了它的來源(refCount()
),並且正在等待迴應。
所以你所描述的情況不應該發生我認爲。最終制作一個演示,演示您的問題。
編輯:
發射都失效的項目和新項目
下面的例子顯示出比聯例子有點不同的功能。如果緩存的響應無效,則無論如何都會發射,然後它也會收到新的值。這意味着用戶接收的一個或兩個值:
- 1值:高速緩存的值
- 2的值:該失效的緩存值,然後新的那會從現在起被高速緩存新鮮值。
的代碼可能如下所示:
let counter = 1;
const RECACHE_INTERVAL = 1000;
function mockDataFetch() {
return Observable.of(counter++)
.delay(200);
}
let source = Observable.defer(() => {
const now = (new Date()).getTime();
return mockDataFetch()
.map(response => {
return {
'timestamp': now,
'response': response,
};
});
});
let updateRequest = source
.publishReplay(1)
.refCount()
.concatMap(value => {
if (value.timestamp + RECACHE_INTERVAL > (new Date()).getTime()) {
return Observable.from([value.response, null]);
} else {
return Observable.of(value.response);
}
})
.takeWhile(value => value);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 0:", val)), 0);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 50:", val)), 50);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 200:", val)), 200);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1200:", val)), 1200);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1300:", val)), 1300);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1500:", val)), 1500);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 3500:", val)), 3500);
見現場演示:https://jsbin.com/ketemi/2/edit?js,console
這將打印到控制檯輸出如下:
Response 0: 1
Response 50: 1
Response 200: 1
Response 1200: 1
Response 1300: 1
Response 1200: 2
Response 1300: 2
Response 1500: 2
Response 3500: 2
Response 3500: 3
通知1200
和1300
收到先將舊的緩存值1
立即a然後用新值2
計算另一個值。
另一方面,1500
只收到新值,因爲2
已被緩存且有效。
最容易混淆的事情可能是爲什麼我使用concatMap().takeWhile()
。這是因爲我需要確保新的響應(不是無效的)是發送完整通知之前的最後一個值,並且可能沒有運營商(對於此用例,first()
和takeWhile()
都不適用)。
僅發射當前項目,而不必等待刷新
另一種使用情況可能是,當我們想在不等待來自HTTP請求響應新的發射只緩存值。
let counter = 1;
const RECACHE_INTERVAL = 1000;
function mockDataFetch() {
return Observable.of(counter++)
.delay(200);
}
let source = Observable.defer(() => {
const now = (new Date()).getTime();
return mockDataFetch()
.map(response => {
return {
'timestamp': now,
'response': response,
};
});
});
let updateRequest = source
.publishReplay(1)
.refCount()
.concatMap((value, i) => {
if (i === 0) {
if (value.timestamp + RECACHE_INTERVAL > (new Date()).getTime()) { // is cached item valid?
return Observable.from([value.response, null]);
} else {
return Observable.of(value.response);
}
}
return Observable.of(null);
})
.takeWhile(value => value);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 0:", val)), 0);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 50:", val)), 50);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 200:", val)), 200);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1200:", val)), 1200);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1300:", val)), 1300);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 1500:", val)), 1500);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 3500:", val)), 3500);
setTimeout(() => updateRequest.subscribe(val => console.log("Response 3800:", val)), 3800);
見現場演示:https://jsbin.com/kebapu/2/edit?js,console
這個例子打印到控制檯:
Response 0: 1
Response 50: 1
Response 200: 1
Response 1200: 1
Response 1300: 1
Response 1500: 2
Response 3500: 2
Response 3800: 3
注意兩個1200
和1300
獲得價值1
,因爲這是緩存值,即使它現在是無效的。第一次調用1200
只是產生一個新的HTTP請求,而不等待它的響應並且只發出緩存的值。然後在1500
新鮮的價值被緩存,所以它只是重新發布。這同樣適用於3500
和3800
。
注意,油1200
用戶會立即收到通知next
但HTTP請求完成後,才complete
通知將被髮送。我們需要等待,因爲如果我們在next
之後立即發送complete
,它會使鏈處置其一次性消息,這也應該取消HTTP請求(這是我們絕對不想做的)。
感謝您的建議,這是非常好的。有兩個問題:我需要從鏈中刪除'.take(1)'運算符,否則它將不會再發出第二個HTTP請求。第二個問題是我希望在RECACHE_INTERVAL之後調用另一個subsribe()時觸發recache,而不是每個RECACHE_INTERVAL。 – Martin
@Martin我已經更新了我的答案,以便只有在RECACHE_INTERVAL後另一個訂閱者訂閱時纔會發出請求。希望這有助於幫助 – Ashley