[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() {
if (this.root) {
this.scheduler.flush();
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) {
try {
fiber.bdom = this.renderFn();
fiber.root!.counter--;
const root = fiber.root!;
root.setCounter(root.counter - 1);
} catch (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 (isFirstRound && fiber && fiber.node.fiber) {
fiber.root!.counter--;
const root = fiber.root!;
root.setCounter(root.counter - 1);
}
return true;
}
+9 -2
View File
@@ -16,7 +16,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
let current = node.fiber;
if (current) {
let root = current.root!;
root.counter = root.counter + 1 - cancelFibers(current.children);
root.setCounter(root.counter + 1 - cancelFibers(current.children));
current.children = [];
current.bdom = null;
if (fibersInError.has(current)) {
@@ -74,7 +74,7 @@ export class Fiber {
if (parent) {
this.deep = parent.deep;
const root = parent.root!;
root.counter++;
root.setCounter(root.counter + 1);
this.root = root;
parent.children.push(this);
} else {
@@ -144,6 +144,13 @@ export class RootFiber extends Fiber {
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";
+26 -43
View File
@@ -11,27 +11,15 @@ export class Scheduler {
// interactions with other code, such as test frameworks that override them
static requestAnimationFrame = window.requestAnimationFrame.bind(window);
tasks: Set<RootFiber> = new Set();
isRunning: boolean = false;
requestAnimationFrame: Window["requestAnimationFrame"];
frame: number = 0;
constructor() {
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
}
start() {
this.isRunning = true;
this.scheduleTasks();
}
stop() {
this.isRunning = false;
}
addFiber(fiber: Fiber) {
this.tasks.add(fiber.root!);
if (!this.isRunning) {
this.start();
}
}
/**
@@ -39,39 +27,34 @@ export class Scheduler {
* Other tasks are left unchanged.
*/
flush() {
this.tasks.forEach((fiber) => {
if (fiber.root !== fiber) {
this.tasks.delete(fiber);
return;
}
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();
if (this.frame === 0) {
this.frame = this.requestAnimationFrame(() => {
this.frame = 0;
this.tasks.forEach((fiber) => this.processFiber(fiber));
});
}
}
scheduleTasks() {
this.requestAnimationFrame(() => {
this.flush();
if (this.isRunning) {
this.scheduleTasks();
processFiber(fiber: RootFiber) {
if (fiber.root !== fiber) {
this.tasks.delete(fiber);
return;
}
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);
}
}
}