Closed mbostock closed 1 week ago
The crucial thing here is that even if another cell uses the same query (SELECT * FROM x WHERE y
), its strings will not be ===
to those of the first query, because a different array will have been created; so no queuing happens between the two independent queries. 👏
Unfortunately I think this approach has a fundamental flaw as exhibited in this example code:
Promise.all([1, 2, 3].map((i) => sql`SELECT ${i}`))
This code will throw Error: invalidated
because the i = 1 and i = 2 cases are “invalidated” by the subsequent i = 3. In other words, the approach in this PR works only if the sql
template literal is invoked only once per code block, and any case in which the sql
template literal exists within a loop will cause problems.
We could still adapt this fix for SQL cells specifically (by changing the generated code to sql.queue
or something), but perhaps I should just look into making the more general runtime-level fix.
Ah… bummer.
If you write Promise.all([1, 2, 3].map((i) => sql(["SELECT", ""], i)))
instead it works, because it's a new array each time. MDN explains the difference: “For any particular tagged template literal expression, the tag function will always be called with the exact same literal array, no matter how many times the literal is evaluated.”
Could we salvage this approach if we somehow passed the variable's v._version
to know if we are running in the same call or in a subsequent one?
I believe this was superseded by #1748
Ref #1469. This uses a WeakMap keyed by the string literal
strings
, effectively creating a queue per SQL cell and persql
tagged template literal. I’d prefer to implement a more general solution that also helps out with e.g. concurrentfetch
(maybe in the Observable Runtime?), but this is an easy fix for probably the most common instance of this problem. The performance of themag
slider on the SQL page is dramatically improved under fast interaction.