Answer the review: share a graph request asked again, by its range and its drawn list
Graph requests were never shared, on the grounds that the scene never asks for the same graph twice. It does: turning the layer off and on while the worker is busy asks again for the graph already waiting. The new request superseded the old one, and the old one's rejection handler, which finds its request by range and drawn list, wiped the state of the new one: the layer stayed on with no graph. An identical request now shares the outstanding promise, a graph being the same when its range matches and its drawn list is the very same array. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
@@ -18,12 +18,6 @@ export class SupersededRequest extends Error {
|
||||
/** A request made and not yet answered: what was asked, and the promise whoever asked is holding. */
|
||||
interface Outstanding {
|
||||
readonly request: RoutingRequest;
|
||||
/**
|
||||
* A route question without its id, so the same route asked for twice can be recognised. Empty for
|
||||
* a graph: the scene never asks for the same graph twice, and spelling out 70 000 drawn stars to
|
||||
* compare costs more than the comparison could save.
|
||||
*/
|
||||
readonly question: string;
|
||||
readonly promise: Promise<RoutingResponse>;
|
||||
readonly resolve: (response: RoutingResponse) => void;
|
||||
readonly reject: (error: Error) => void;
|
||||
@@ -36,9 +30,19 @@ function outstanding(request: RoutingRequest): Outstanding {
|
||||
resolve = onResolve;
|
||||
reject = onReject;
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { requestId, ...question } = request;
|
||||
return { request, question: request.kind === 'route' ? JSON.stringify(question) : '', promise, resolve, reject };
|
||||
return { request, promise, resolve, reject };
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether two requests ask the same question. A graph is the same when it is for the same range and
|
||||
* the very same list of drawn stars: the star field replaces that list whenever the set changes, so
|
||||
* one array is one set, and comparing 70 000 indices would cost more than sharing could save.
|
||||
*/
|
||||
function asksTheSame(a: RoutingRequest, b: RoutingRequest): boolean {
|
||||
if (a.kind === 'links' || b.kind === 'links') {
|
||||
return a.kind === 'links' && b.kind === 'links' && a.rangePc === b.rangePc && a.drawn === b.drawn;
|
||||
}
|
||||
return a.fromId === b.fromId && a.toId === b.toId && a.rangePc === b.rangePc && a.ceilingPc === b.ceilingPc;
|
||||
}
|
||||
|
||||
/** The routing worker, where this environment has one. */
|
||||
@@ -50,12 +54,13 @@ function startRoutingWorker(): Worker | undefined {
|
||||
* Asks the route questions of a worker holding its own copy of the catalogue, and hands back
|
||||
* promises.
|
||||
*
|
||||
* The worker answers one request at a time and cannot drop one it has started: a jump-link graph
|
||||
* at 8 pc is seconds of work. So requests are held here and sent one by one, and while one is out,
|
||||
* only the latest of each kind waits behind it — a newer graph replaces an older one before it is
|
||||
* ever built, and the older promise is rejected with {@link SupersededRequest}. Routes go ahead of
|
||||
* graphs, being quick and asked for by a click. The same route asked for again while it is still
|
||||
* outstanding shares the answer rather than being worked out twice.
|
||||
* The worker answers one request at a time and cannot drop one it has started: a route with no path
|
||||
* can be seconds of work, and a graph of the drawn stars at 8 pc a few hundred milliseconds. So
|
||||
* requests are held here and sent one by one, and while one is out, only the latest of each kind
|
||||
* waits behind it — a newer graph replaces an older one before it is ever built, and the older
|
||||
* promise is rejected with {@link SupersededRequest}. Routes go ahead of graphs, being quick to ask
|
||||
* for and asked for by a click. The same question asked again while it is still outstanding shares
|
||||
* the answer rather than being worked out twice; see `asksTheSame`.
|
||||
*
|
||||
* Where there is no worker — the unit tests' DOM has none, and a worker can fail to load or crash —
|
||||
* the same answers are worked out in place, from the index the scene already holds.
|
||||
@@ -115,11 +120,13 @@ export class RoutingClient {
|
||||
if (!this.worker) {
|
||||
return new Promise((resolve) => resolve(answerRouting(this.localIndex, request)));
|
||||
}
|
||||
const asked = outstanding(request);
|
||||
const same = asked.question ? [this.inFlight, this.waiting[request.kind]].find((other) => other?.question === asked.question) : undefined;
|
||||
// Shared rather than replaced: an identical request superseding the one it repeats would reject it,
|
||||
// and whoever holds that promise would take the rejection for its own question.
|
||||
const same = [this.inFlight, this.waiting[request.kind]].find((other) => other !== undefined && asksTheSame(other.request, request));
|
||||
if (same) {
|
||||
return same.promise;
|
||||
}
|
||||
const asked = outstanding(request);
|
||||
this.waiting[request.kind]?.reject(new SupersededRequest());
|
||||
this.waiting[request.kind] = asked;
|
||||
this.sendNext();
|
||||
|
||||
Reference in New Issue
Block a user