Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ export default class StoreImp implements Store {
*/
private readonly _taskScheduler: TaskScheduler

/**
* Indicators waiting for a coalesced recalculation flush
*/
private readonly _pendingCalcIndicators = new Map<string, IndicatorImp>()

private _calcFlushScheduled = false

/**
* Overlay
*/
Expand Down Expand Up @@ -1343,11 +1350,31 @@ export default class StoreImp implements Store {
let indicators: IndicatorImp[] = []
indicators = indicators.concat(data)
if (indicators.length > 0) {
const tasks: Record<string, Promise<unknown>> = {}
indicators.forEach((indicator) => {
tasks[indicator.id] = indicator.calcImp(this._dataList)
this._pendingCalcIndicators.set(indicator.id, indicator)
})
this._taskScheduler.add(tasks)
if (!this._calcFlushScheduled) {
this._calcFlushScheduled = true
// `calc` runs synchronously inside `calcImp`, so a burst of synchronous
// data updates (e.g. a websocket catch-up) would recalculate every
// indicator over the whole history once per update, in one blocking
// task. Defer the recalculation to a microtask: `calc` is a pure
// function of the current data list, so one flush after the last
// update of the tick produces the same result as recalculating after
// each of them.
void Promise.resolve().then(() => {
this._calcFlushScheduled = false
if (this._pendingCalcIndicators.size === 0) {
return
}
const tasks: Record<string, Promise<unknown>> = {}
this._pendingCalcIndicators.forEach((indicator) => {
tasks[indicator.id] = indicator.calcImp(this._dataList)
})
this._pendingCalcIndicators.clear()
this._taskScheduler.add(tasks)
})
}
}
}

Expand Down Expand Up @@ -1798,6 +1825,8 @@ export default class StoreImp implements Store {
destroy(): void {
this._clearData()
this._clearLastPriceMarkExtendTextUpdateTimer()
this._calcFlushScheduled = false
this._pendingCalcIndicators.clear()
this._taskScheduler.clear()
this._overlays.clear()
this._indicators.clear()
Expand Down