From c68e04ff9c0eae592a4c6eec27c45606fb271a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Sun, 2 Jun 2024 13:08:09 -0700 Subject: [PATCH] WIP: hook up scheduler --- packages/qwik/src/core/v2/shared/scheduler.ts | 11 ++++++- .../qwik/src/core/v2/signal-v2/v2-signal.ts | 32 +++++++++++++++---- .../src/core/v2/signal-v2/v2-signal.unit.tsx | 9 ++---- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/packages/qwik/src/core/v2/shared/scheduler.ts b/packages/qwik/src/core/v2/shared/scheduler.ts index e2826cc5722..3c3648d564e 100644 --- a/packages/qwik/src/core/v2/shared/scheduler.ts +++ b/packages/qwik/src/core/v2/shared/scheduler.ts @@ -109,6 +109,9 @@ export const enum ChoreType { /* order of elements (not encoded here) */ MICRO /* ***************** */ = 0b000_111, + /** Ensure tha the QRL promise is resolved before processing next chores in the queue */ + QRL_RESOLVE /* *********** */ = 0b000_000, + // TODO(mhevery): COMPUTED should be deleted because it is handled synchronously. COMPUTED /* ************** */ = 0b000_001, RESOURCE /* ************** */ = 0b000_010, TASK /* ****************** */ = 0b000_011, @@ -150,6 +153,12 @@ export const createScheduler = ( //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// + function schedule( + type: ChoreType.QRL_RESOLVE, + ignore0: null, + ignore1: null, + promise: Promise + ): ValueOrPromise; function schedule(type: ChoreType.JOURNAL_FLUSH): ValueOrPromise; function schedule(type: ChoreType.WAIT_FOR_ALL): ValueOrPromise; function schedule(type: ChoreType.WAIT_FOR_COMPONENTS): ValueOrPromise; @@ -186,7 +195,7 @@ export const createScheduler = ( ///// IMPLEMENTATION ///// function schedule( type: ChoreType, - hostOrTask: HostElement | Task = null!, + hostOrTask: HostElement | Task | null = null, targetOrQrl: HostElement | QRL<(...args: any[]) => any> | null = null, payload: any = null ): ValueOrPromise { diff --git a/packages/qwik/src/core/v2/signal-v2/v2-signal.ts b/packages/qwik/src/core/v2/signal-v2/v2-signal.ts index 6b0873300a0..b046995ec96 100644 --- a/packages/qwik/src/core/v2/signal-v2/v2-signal.ts +++ b/packages/qwik/src/core/v2/signal-v2/v2-signal.ts @@ -59,14 +59,32 @@ class Signal2 implements ISignal2 { * * An effect is work which needs to be done when the signal changes. * - * 1. `Task` - A task which needs to be re-run. + * 1. `Task` - A task which needs to be re-run. For example a `useTask` or `useResource`, etc... * 2. `VNode` - A component or Direct DOM update. (Look at the VNode attributes to determine if it is * a Component or VNode signal target) + * 3. `Signal2` - A derived signal which needs to be re-computed. A derived signal gets marked as + * dirty synchronously, but the computation is lazy. + * + * `Task` and `VNode` are leaves in a tree, where as `Signal2` is a node in a tree. When + * processing a change in a signal, the leaves (`Task` and `VNode`) are scheduled for execution, + * where as the Nodes (`Signal2`) are synchronously recursed into and marked as dirty. */ private $effects$: null | Array = null; - /** If this signal is computed, then compute function is stored here. */ + /** + * If this signal is computed, then compute function is stored here. + * + * The computed functions must be executed synchronously (because of this we need to eagerly + * resolve the QRL during the mark dirty phase so that any call to it will be synchronous). ) + */ private $computeQrl$: null | QRLInternal<() => T>; + + /** + * The execution context when the signal was being created. + * + * The context contains the scheduler and the subscriber, and is used by the derived signal to + * capture dependencies. + */ private $context$: InvokeContext | undefined; constructor(value: T, computeTask: QRLInternal<() => T> | null) { @@ -113,12 +131,12 @@ class Signal2 implements ISignal2 { assertDefined(subscriber.$computeQrl$, 'Expecting ComputedSignal'); // Special case of a computed signal. subscriber.$untrackedValue$ = NEEDS_COMPUTATION; - const resolved = subscriber.$computeQrl$.getFn(); - // TODO(mhevery): This needs to be added to the scheduler to make sure - // that we don't try to read the computed signal until it has been resolved. - // scheduler(ChoreType.QRL_RESOLVE, resolved); + const qrl = subscriber.$computeQrl$!; + if (!qrl.resolved) { + const resolved = subscriber.$computeQrl$.resolve(); + this.$context$?.$container2$?.$scheduler$(ChoreType.QRL_RESOLVE, null, null, resolved); + } target = subscriber; - DEBUG && log('Should schedule', resolved); } else { target = subscriber[1] as Task; assertTrue(isTask(target), 'Invalid subscriber.'); diff --git a/packages/qwik/src/core/v2/signal-v2/v2-signal.unit.tsx b/packages/qwik/src/core/v2/signal-v2/v2-signal.unit.tsx index dda07504c04..5f397b22f85 100644 --- a/packages/qwik/src/core/v2/signal-v2/v2-signal.unit.tsx +++ b/packages/qwik/src/core/v2/signal-v2/v2-signal.unit.tsx @@ -51,15 +51,10 @@ describe('v2-signal', () => { const a = createSignal2(2); const b = createSignal2(10); await retry(() => { - const signal = createComputed2$(() => { - return a.value + b.value; - }); + const signal = createComputed2$(() => a.value + b.value); expect((signal as any).$untrackedValue$).toEqual(12); expect(signal.value).toEqual(12); - effect$(() => { - console.log('TEST effect.signal', signal.value); - log.push(signal.value); - }); + effect$(() => log.push(signal.value)); expect(log).toEqual([12]); a.value++; b.value += 10;