feat(presentation): add cancellable frame renderer
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import type { FrameScheduler } from './ui-contracts.ts';
|
||||
|
||||
class FrameRendererFault extends Error {
|
||||
readonly errors: readonly unknown[];
|
||||
|
||||
constructor(errors: readonly unknown[]) {
|
||||
super('FrameRenderer render and disposal failed');
|
||||
this.name = 'FrameRendererFault';
|
||||
this.errors = Object.freeze([...errors]);
|
||||
}
|
||||
}
|
||||
|
||||
export class FrameRenderer {
|
||||
private pending: (() => void) | null = null;
|
||||
private generation = 0;
|
||||
private disposed = false;
|
||||
|
||||
public constructor(
|
||||
private readonly scheduler: FrameScheduler,
|
||||
private readonly render: () => void,
|
||||
private readonly onFault: (error: unknown) => void,
|
||||
) {}
|
||||
|
||||
public request(): void {
|
||||
if (this.disposed || this.pending !== null) return;
|
||||
|
||||
const generation = this.generation;
|
||||
this.pending = this.scheduler.request(() => {
|
||||
if (this.disposed || generation !== this.generation) return;
|
||||
this.pending = null;
|
||||
this.generation += 1;
|
||||
try {
|
||||
this.render();
|
||||
} catch (error) {
|
||||
let fault: unknown = error;
|
||||
try {
|
||||
this.dispose();
|
||||
} catch (cancelError) {
|
||||
fault = new FrameRendererFault([error, cancelError]);
|
||||
}
|
||||
this.onFault(fault);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public flush(): void {
|
||||
if (this.disposed) return;
|
||||
|
||||
const cancel = this.pending;
|
||||
this.pending = null;
|
||||
this.generation += 1;
|
||||
const generation = this.generation;
|
||||
if (cancel !== null) cancel();
|
||||
if (this.disposed || generation !== this.generation) return;
|
||||
this.render();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.disposed) return;
|
||||
this.disposed = true;
|
||||
|
||||
const cancel = this.pending;
|
||||
this.pending = null;
|
||||
this.generation += 1;
|
||||
if (cancel !== null) cancel();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user