[REF] component: introduce RootFiber.setCounter and update scheduler

The goal is to be able to execute code whenever a root fiber is ready,
and before the next animation frame
This commit is contained in:
Géry Debongnie
2022-03-28 14:02:35 +02:00
committed by Samuel Degueldre
parent d80fad760c
commit 828be28653
5 changed files with 40 additions and 47 deletions
+1
View File
@@ -101,6 +101,7 @@ export class App<
destroy() { destroy() {
if (this.root) { if (this.root) {
this.scheduler.flush();
this.root.destroy(); this.root.destroy();
} }
} }
+2 -1
View File
@@ -245,7 +245,8 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
_render(fiber: Fiber | RootFiber) { _render(fiber: Fiber | RootFiber) {
try { try {
fiber.bdom = this.renderFn(); fiber.bdom = this.renderFn();
fiber.root!.counter--; const root = fiber.root!;
root.setCounter(root.counter - 1);
} catch (e) { } catch (e) {
handleError({ node: this, error: e }); handleError({ node: this, error: e });
} }
+2 -1
View File
@@ -30,7 +30,8 @@ function _handleError(node: ComponentNode | null, error: any, isFirstRound = fal
if (stopped) { if (stopped) {
if (isFirstRound && fiber && fiber.node.fiber) { if (isFirstRound && fiber && fiber.node.fiber) {
fiber.root!.counter--; const root = fiber.root!;
root.setCounter(root.counter - 1);
} }
return true; return true;
} }
+9 -2
View File
@@ -16,7 +16,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
let current = node.fiber; let current = node.fiber;
if (current) { if (current) {
let root = current.root!; let root = current.root!;
root.counter = root.counter + 1 - cancelFibers(current.children); root.setCounter(root.counter + 1 - cancelFibers(current.children));
current.children = []; current.children = [];
current.bdom = null; current.bdom = null;
if (fibersInError.has(current)) { if (fibersInError.has(current)) {
@@ -74,7 +74,7 @@ export class Fiber {
if (parent) { if (parent) {
this.deep = parent.deep; this.deep = parent.deep;
const root = parent.root!; const root = parent.root!;
root.counter++; root.setCounter(root.counter + 1);
this.root = root; this.root = root;
parent.children.push(this); parent.children.push(this);
} else { } else {
@@ -144,6 +144,13 @@ export class RootFiber extends Fiber {
handleError({ fiber: current || this, error: e }); handleError({ fiber: current || this, error: e });
} }
} }
setCounter(newValue: number) {
this.counter = newValue;
if (newValue === 0) {
this.node.app.scheduler.flush();
}
}
} }
type Position = "first-child" | "last-child"; type Position = "first-child" | "last-child";
+26 -43
View File
@@ -11,27 +11,15 @@ export class Scheduler {
// interactions with other code, such as test frameworks that override them // interactions with other code, such as test frameworks that override them
static requestAnimationFrame = window.requestAnimationFrame.bind(window); static requestAnimationFrame = window.requestAnimationFrame.bind(window);
tasks: Set<RootFiber> = new Set(); tasks: Set<RootFiber> = new Set();
isRunning: boolean = false;
requestAnimationFrame: Window["requestAnimationFrame"]; requestAnimationFrame: Window["requestAnimationFrame"];
frame: number = 0;
constructor() { constructor() {
this.requestAnimationFrame = Scheduler.requestAnimationFrame; this.requestAnimationFrame = Scheduler.requestAnimationFrame;
} }
start() {
this.isRunning = true;
this.scheduleTasks();
}
stop() {
this.isRunning = false;
}
addFiber(fiber: Fiber) { addFiber(fiber: Fiber) {
this.tasks.add(fiber.root!); this.tasks.add(fiber.root!);
if (!this.isRunning) {
this.start();
}
} }
/** /**
@@ -39,39 +27,34 @@ export class Scheduler {
* Other tasks are left unchanged. * Other tasks are left unchanged.
*/ */
flush() { flush() {
this.tasks.forEach((fiber) => { if (this.frame === 0) {
if (fiber.root !== fiber) { this.frame = this.requestAnimationFrame(() => {
this.tasks.delete(fiber); this.frame = 0;
return; this.tasks.forEach((fiber) => this.processFiber(fiber));
} });
const hasError = fibersInError.has(fiber);
if (hasError && fiber.counter !== 0) {
this.tasks.delete(fiber);
return;
}
if (fiber.node.status === STATUS.DESTROYED) {
this.tasks.delete(fiber);
return;
}
if (fiber.counter === 0) {
if (!hasError) {
fiber.complete();
}
this.tasks.delete(fiber);
}
});
if (this.tasks.size === 0) {
this.stop();
} }
} }
scheduleTasks() { processFiber(fiber: RootFiber) {
this.requestAnimationFrame(() => { if (fiber.root !== fiber) {
this.flush(); this.tasks.delete(fiber);
if (this.isRunning) { return;
this.scheduleTasks(); }
const hasError = fibersInError.has(fiber);
if (hasError && fiber.counter !== 0) {
this.tasks.delete(fiber);
return;
}
if (fiber.node.status === STATUS.DESTROYED) {
this.tasks.delete(fiber);
return;
}
if (fiber.counter === 0) {
if (!hasError) {
fiber.complete();
} }
}); this.tasks.delete(fiber);
}
} }
} }