Compare commits

...

1 Commits

Author SHA1 Message Date
Samuel Degueldre aef6923c23 PROTOTYPE: batch renders in animation frame instead of microtick 2022-02-23 11:55:53 +01:00
2 changed files with 56 additions and 40 deletions
+28 -9
View File
@@ -120,20 +120,30 @@ export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E
this.initiateRender(fiber); this.initiateRender(fiber);
} }
async initiateRender(fiber: Fiber | MountFiber) { initiateRender(fiber: Fiber | MountFiber) {
this.fiber = fiber; this.fiber = fiber;
if (this.mounted.length) { if (this.mounted.length) {
fiber.root!.mounted.push(fiber); fiber.root!.mounted.push(fiber);
} }
const component = this.component; const component = this.component;
const finish = () => {
if (this.status === STATUS.NEW && this.fiber === fiber) {
this._render(fiber);
}
}
let willStartResults;
try { try {
await Promise.all(this.willStart.map((f) => f.call(component))); willStartResults = this.willStart.map((f) => f.call(component));
} catch (e) { } catch (e) {
handleError({ node: this, error: e }); handleError({ node: this, error: e });
return; return;
} }
if (this.status === STATUS.NEW && this.fiber === fiber) { if (willStartResults.some(result => result instanceof Promise)) {
this._render(fiber); Promise.all(willStartResults)
.then(finish)
.catch(e => handleError({ node: this, error: e }));
} else {
finish();
} }
} }
@@ -153,8 +163,8 @@ export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E
const fiber = makeRootFiber(this); const fiber = makeRootFiber(this);
this.fiber = fiber; this.fiber = fiber;
this.app.scheduler.addFiber(fiber); debugger;
await Promise.resolve(); window.requestAnimationFrame(() => {
if (this.status === STATUS.DESTROYED) { if (this.status === STATUS.DESTROYED) {
return; return;
} }
@@ -172,6 +182,8 @@ export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E
if (this.fiber === fiber && (current || !fiber.parent)) { if (this.fiber === fiber && (current || !fiber.parent)) {
this._render(fiber); this._render(fiber);
} }
});
this.app.scheduler.addFiber(fiber);
} }
_render(fiber: Fiber | RootFiber) { _render(fiber: Fiber | RootFiber) {
@@ -207,14 +219,14 @@ export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E
this.status = STATUS.DESTROYED; this.status = STATUS.DESTROYED;
} }
async updateAndRender(props: any, parentFiber: Fiber) { updateAndRender(props: any, parentFiber: Fiber) {
// update // update
const fiber = makeChildFiber(this, parentFiber); const fiber = makeChildFiber(this, parentFiber);
this.fiber = fiber; this.fiber = fiber;
const component = this.component; const component = this.component;
applyDefaultProps(props, component.constructor as any); applyDefaultProps(props, component.constructor as any);
const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
await prom; const finish = () => {
if (fiber !== this.fiber) { if (fiber !== this.fiber) {
return; return;
} }
@@ -228,6 +240,13 @@ export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E
parentRoot.patched.push(fiber); parentRoot.patched.push(fiber);
} }
} }
const willUpdatePropsResults = this.willUpdateProps.map((f) => f.call(component, props));
if (willUpdatePropsResults.some(res => res instanceof Promise)) {
Promise.all(willUpdatePropsResults).then(finish);
} else {
finish();
}
}
/** /**
* Finds a child that has dom that is not yet updated, and update it. This * Finds a child that has dom that is not yet updated, and update it. This
+2 -5
View File
@@ -97,17 +97,14 @@ test("destroying/recreating a subwidget with different props (if start is not ov
expect(["W:setup", "W:willStart", "W:willRender", "W:rendered", "W:mounted"]).toBeLogged(); expect(["W:setup", "W:willStart", "W:willRender", "W:rendered", "W:mounted"]).toBeLogged();
expect(n).toBe(0); expect(n).toBe(0);
w.state.val = 2; w.state.val = 2;
await nextMicroTick(); await new Promise(requestAnimationFrame); // calls to _render are batched per animation frame
await nextMicroTick();
expect(n).toBe(1); expect(n).toBe(1);
expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged(); expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged();
w.state.val = 3; w.state.val = 3;
await nextMicroTick(); await new Promise(requestAnimationFrame);
await nextMicroTick();
expect(n).toBe(2); expect(n).toBe(2);
expect([ expect([