Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On every single-bar data update (
subscribeBarcallback →Store._addData→_calcIndicator),each indicator is recalculated over the entire data list:
calcruns synchronously insidecalcImp(await this.calc(dataList, this)— the usercallback executes before any await), so a burst of synchronous updates — a websocket catch-up
delivering 100 ticks — performs 100 full recalculations (for VOL:
dataList.map(...)over allbars, allocating a complete new result array each time) in one blocking main-thread task.
Measured on
main(production UMD, Chromium, 10 000 bars, candles + VOL, burst of 100synchronous
subscribeBarupdates, instrumented_calcIndicator):calcinvocationscalcThis is the library's weakest scenario in a head-to-head benchmark against lightweight-charts
(7.7× slower on a 100-update burst, 3.1× peak heap), while initial render, pan/zoom/hover and
steady-state streaming are at parity or better.
Fix
Coalesce the recalculation into a microtask flush. Marking is immediate; the actual
calccalls happen once per microtask, over the current data list:
This is safe because
calcis a pure function of the data list (plus the indicator itself):one flush after the last update of a synchronous burst produces the same result as
recalculating after every intermediate update — the intermediate results were never rendered.
The TaskScheduler and its layout callback are unchanged, so the flush → recalculate → layout
ordering is preserved (one microtask later than before).
State is cleared in
destroy().Result (same instrumented setup)
calcinvocationsVerification
pnpm code-lint— pass (154 files, no fixes)pnpm type-check— passpnpm build-esm— passproduces a long task; steady-state 10–20 Hz streaming unchanged.
Notes
asynchronous downstream (TaskScheduler → layout).
harmlessly (the flush skips empty pending state).
would see one call per microtask instead —
calcis documented as a pure calculation of thedata list, and render-consumed state is exactly what this preserves.