80
80
* declaration order within component.
81
81
*/
82
82
83
- import { assertEqual } from './error/assert' ;
84
- import type { QRLInternal } from './qrl/qrl-class' ;
85
- import type { JSXOutput } from './jsx/types/jsx-node' ;
86
- import { Task , TaskFlags , cleanupTask , runTask , type TaskFn } from '../use/use-task' ;
87
- import { runResource , type ResourceDescriptor } from '../use/use-resource' ;
88
- import { logWarn } from './utils/log' ;
89
- import { isPromise , maybeThenPassError , retryOnPromise , safeCall } from './utils/promises' ;
90
- import type { ValueOrPromise } from './utils/types' ;
91
- import { isDomContainer } from '../client/dom-container' ;
83
+ import { isDomContainer , type DomContainer } from '../client/dom-container' ;
92
84
import {
93
85
ElementVNodeProps ,
94
86
VNodeFlags ,
@@ -97,21 +89,28 @@ import {
97
89
type ElementVNode ,
98
90
type VirtualVNode ,
99
91
} from '../client/types' ;
100
- import { vnode_isVNode , vnode_setAttr , VNodeJournalOpCode } from '../client/vnode' ;
92
+ import { VNodeJournalOpCode , vnode_isVNode , vnode_setAttr } from '../client/vnode' ;
101
93
import { vnode_diff } from '../client/vnode-diff' ;
102
- import { executeComponent } from './component-execution' ;
103
- import type { Container , HostElement } from './types' ;
94
+ import { triggerEffects , type ComputedSignal , type WrappedSignal } from '../signal/signal' ;
104
95
import { isSignal , type Signal } from '../signal/signal.public' ;
105
- import { type DomContainer } from '../client/dom-container' ;
106
- import { serializeAttribute } from './utils/styles' ;
96
+ import type { TargetType } from '../signal/store' ;
97
+ import type { ISsrNode } from '../ssr/ssr-types' ;
98
+ import { runResource , type ResourceDescriptor } from '../use/use-resource' ;
99
+ import { Task , TaskFlags , cleanupTask , runTask , type TaskFn } from '../use/use-task' ;
100
+ import { executeComponent } from './component-execution' ;
107
101
import type { OnRenderFn } from './component.public' ;
102
+ import { assertEqual } from './error/assert' ;
108
103
import type { Props } from './jsx/jsx-runtime' ;
104
+ import type { JSXOutput } from './jsx/types/jsx-node' ;
105
+ import type { QRLInternal } from './qrl/qrl-class' ;
106
+ import { ssrNodeDocumentPosition , vnode_documentPosition } from './scheduler-document-position' ;
107
+ import type { Container , HostElement } from './types' ;
108
+ import { logWarn } from './utils/log' ;
109
109
import { QScopedStyle } from './utils/markers' ;
110
+ import { isPromise , retryOnPromise , safeCall } from './utils/promises' ;
110
111
import { addComponentStylePrefix } from './utils/scoped-styles' ;
111
- import { type WrappedSignal , type ComputedSignal , triggerEffects } from '../signal/signal' ;
112
- import type { TargetType } from '../signal/store' ;
113
- import { ssrNodeDocumentPosition , vnode_documentPosition } from './scheduler-document-position' ;
114
- import type { ISsrNode } from '../ssr/ssr-types' ;
112
+ import { serializeAttribute } from './utils/styles' ;
113
+ import type { ValueOrPromise } from './utils/types' ;
115
114
116
115
// Turn this on to get debug output of what the scheduler is doing.
117
116
const DEBUG : boolean = false ;
@@ -150,7 +149,7 @@ export interface Chore {
150
149
$target$ : ChoreTarget | null ;
151
150
$payload$ : unknown ;
152
151
$resolve$ : ( value : any ) => void ;
153
- $promise$ : Promise < any > ;
152
+ $promise$ ? : Promise < any > ;
154
153
$returnValue$ : any ;
155
154
$executed$ : boolean ;
156
155
}
@@ -168,6 +167,10 @@ export type Scheduler = ReturnType<typeof createScheduler>;
168
167
169
168
type ChoreTarget = HostElement | QRLInternal < ( ...args : unknown [ ] ) => unknown > | Signal | TargetType ;
170
169
170
+ const getPromise = ( chore : Chore ) => {
171
+ return ( chore . $promise$ ||= new Promise ( ( resolve ) => ( chore . $resolve$ = resolve ) ) ) ;
172
+ } ;
173
+
171
174
export const createScheduler = (
172
175
container : Container ,
173
176
scheduleDrain : ( ) => void ,
@@ -273,7 +276,6 @@ export const createScheduler = (
273
276
$returnValue$ : null ,
274
277
$executed$ : false ,
275
278
} ;
276
- chore . $promise$ = new Promise ( ( resolve ) => ( chore . $resolve$ = resolve ) ) ;
277
279
DEBUG && debugTrace ( 'schedule' , chore , currentChore , choreQueue ) ;
278
280
chore = sortedInsert ( choreQueue , chore , ( container as DomContainer ) . rootVNode || null ) ;
279
281
if ( ! journalFlushScheduled && runLater ) {
@@ -283,7 +285,7 @@ export const createScheduler = (
283
285
scheduleDrain ( ) ;
284
286
}
285
287
if ( runLater ) {
286
- return chore . $promise$ ;
288
+ return getPromise ( chore ) ;
287
289
} else {
288
290
return drainUpTo ( chore , ( container as DomContainer ) . rootVNode || null ) ;
289
291
}
@@ -301,7 +303,7 @@ export const createScheduler = (
301
303
}
302
304
if ( currentChore ) {
303
305
// Already running chore, which means we're waiting for async completion
304
- return runUptoChore . $promise$ ;
306
+ return getPromise ( currentChore ) . then ( ( ) => drainUpTo ( runUptoChore , rootVNode ) ) ;
305
307
}
306
308
while ( choreQueue . length ) {
307
309
const nextChore = choreQueue [ 0 ] ;
@@ -327,9 +329,10 @@ export const createScheduler = (
327
329
continue ;
328
330
}
329
331
const returnValue = executeChore ( nextChore ) ;
330
- if ( isPromise ( returnValue ) ) {
331
- const promise = returnValue . then ( ( ) => drainUpTo ( runUptoChore , rootVNode ) ) ;
332
- return promise ;
332
+ if ( currentChore ) {
333
+ // We're waiting for the returnValue promise
334
+ // Continue draining after it is resolved
335
+ return returnValue . then ( ( ) => drainUpTo ( runUptoChore , rootVNode ) ) ;
333
336
}
334
337
}
335
338
return runUptoChore . $returnValue$ ;
@@ -376,12 +379,18 @@ export const createScheduler = (
376
379
) ;
377
380
break ;
378
381
case ChoreType . RESOURCE :
379
- // Don't await the return value of the resource, because async resources should not be awaited.
380
- // The reason for this is that we should be able to update for example a node with loading
381
- // text. If we await the resource, the loading text will not be displayed until the resource
382
- // is loaded.
383
- const result = runResource ( chore . $payload$ as ResourceDescriptor < TaskFn > , container , host ) ;
384
- returnValue = isDomContainer ( container ) ? null : result ;
382
+ {
383
+ // Don't await the return value of the resource, because async resources should not be awaited.
384
+ // The reason for this is that we should be able to update for example a node with loading
385
+ // text. If we await the resource, the loading text will not be displayed until the resource
386
+ // is loaded.
387
+ const result = runResource (
388
+ chore . $payload$ as ResourceDescriptor < TaskFn > ,
389
+ container ,
390
+ host
391
+ ) ;
392
+ returnValue = isDomContainer ( container ) ? null : result ;
393
+ }
385
394
break ;
386
395
case ChoreType . RUN_QRL :
387
396
{
@@ -396,66 +405,84 @@ export const createScheduler = (
396
405
returnValue = runTask ( chore . $payload$ as Task < TaskFn , TaskFn > , container , host ) ;
397
406
break ;
398
407
case ChoreType . CLEANUP_VISIBLE :
399
- const task = chore . $payload$ as Task < TaskFn , TaskFn > ;
400
- cleanupTask ( task ) ;
408
+ {
409
+ const task = chore . $payload$ as Task < TaskFn , TaskFn > ;
410
+ cleanupTask ( task ) ;
411
+ }
401
412
break ;
402
413
case ChoreType . NODE_DIFF :
403
- const parentVirtualNode = chore . $target$ as VirtualVNode ;
404
- let jsx = chore . $payload$ as JSXOutput ;
405
- if ( isSignal ( jsx ) ) {
406
- jsx = jsx . value as any ;
414
+ {
415
+ const parentVirtualNode = chore . $target$ as VirtualVNode ;
416
+ let jsx = chore . $payload$ as JSXOutput ;
417
+ if ( isSignal ( jsx ) ) {
418
+ jsx = jsx . value as any ;
419
+ }
420
+ returnValue = retryOnPromise ( ( ) =>
421
+ vnode_diff ( container as DomContainer , jsx , parentVirtualNode , null )
422
+ ) ;
407
423
}
408
- returnValue = retryOnPromise ( ( ) =>
409
- vnode_diff ( container as DomContainer , jsx , parentVirtualNode , null )
410
- ) ;
411
424
break ;
412
425
case ChoreType . NODE_PROP :
413
- const virtualNode = chore . $host$ as unknown as ElementVNode ;
414
- const payload = chore . $payload$ as NodePropPayload ;
415
- let value : Signal < any > | string = payload . $value$ ;
416
- if ( isSignal ( value ) ) {
417
- value = value . value as any ;
418
- }
419
- const isConst = payload . $isConst$ ;
420
- const journal = ( container as DomContainer ) . $journal$ ;
421
- const property = chore . $idx$ as string ;
422
- const serializedValue = serializeAttribute ( property , value , payload . $scopedStyleIdPrefix$ ) ;
423
- if ( isConst ) {
424
- const element = virtualNode [ ElementVNodeProps . element ] as Element ;
425
- journal . push ( VNodeJournalOpCode . SetAttribute , element , property , serializedValue ) ;
426
- } else {
427
- vnode_setAttr ( journal , virtualNode , property , serializedValue ) ;
426
+ {
427
+ const virtualNode = chore . $host$ as unknown as ElementVNode ;
428
+ const payload = chore . $payload$ as NodePropPayload ;
429
+ let value : Signal < any > | string = payload . $value$ ;
430
+ if ( isSignal ( value ) ) {
431
+ value = value . value as any ;
432
+ }
433
+ const isConst = payload . $isConst$ ;
434
+ const journal = ( container as DomContainer ) . $journal$ ;
435
+ const property = chore . $idx$ as string ;
436
+ const serializedValue = serializeAttribute (
437
+ property ,
438
+ value ,
439
+ payload . $scopedStyleIdPrefix$
440
+ ) ;
441
+ if ( isConst ) {
442
+ const element = virtualNode [ ElementVNodeProps . element ] as Element ;
443
+ journal . push ( VNodeJournalOpCode . SetAttribute , element , property , serializedValue ) ;
444
+ } else {
445
+ vnode_setAttr ( journal , virtualNode , property , serializedValue ) ;
446
+ }
428
447
}
429
448
break ;
430
449
case ChoreType . QRL_RESOLVE : {
431
- const target = chore . $target$ as QRLInternal < any > ;
432
- returnValue = ! target . resolved ? target . resolve ( ) : null ;
450
+ {
451
+ const target = chore . $target$ as QRLInternal < any > ;
452
+ returnValue = ! target . resolved ? target . resolve ( ) : null ;
453
+ }
433
454
break ;
434
455
}
435
456
case ChoreType . RECOMPUTE_AND_SCHEDULE_EFFECTS : {
436
- const target = chore . $target$ as ComputedSignal < unknown > | WrappedSignal < unknown > ;
437
- const forceRunEffects = target . $forceRunEffects$ ;
438
- target . $forceRunEffects$ = false ;
439
- if ( ! target . $effects$ ?. length ) {
440
- break ;
441
- }
442
- returnValue = retryOnPromise ( ( ) => {
443
- if ( target . $computeIfNeeded$ ( ) || forceRunEffects ) {
444
- triggerEffects ( container , target , target . $effects$ ) ;
457
+ {
458
+ const target = chore . $target$ as ComputedSignal < unknown > | WrappedSignal < unknown > ;
459
+ const forceRunEffects = target . $forceRunEffects$ ;
460
+ target . $forceRunEffects$ = false ;
461
+ if ( ! target . $effects$ ?. length ) {
462
+ break ;
445
463
}
446
- } ) ;
464
+ returnValue = retryOnPromise ( ( ) => {
465
+ if ( target . $computeIfNeeded$ ( ) || forceRunEffects ) {
466
+ triggerEffects ( container , target , target . $effects$ ) ;
467
+ }
468
+ } ) ;
469
+ }
447
470
break ;
448
471
}
449
472
}
450
- return maybeThenPassError ( returnValue , ( value ) => {
451
- if ( currentChore ) {
452
- currentChore . $executed$ = true ;
453
- currentChore . $resolve$ ?.( value ) ;
454
- }
455
- DEBUG && debugTrace ( 'execute.DONE' , null , currentChore , choreQueue ) ;
473
+ const after = ( value : any ) => {
456
474
currentChore = null ;
457
- return ( chore . $returnValue$ = value ) ;
458
- } ) ;
475
+ chore . $executed$ = true ;
476
+ chore . $returnValue$ = value ;
477
+ DEBUG && debugTrace ( 'execute.DONE' , null , chore , choreQueue ) ;
478
+ chore . $resolve$ ?.( value ) ;
479
+ return value ;
480
+ } ;
481
+ if ( isPromise ( returnValue ) ) {
482
+ chore . $promise$ = returnValue ;
483
+ return returnValue . then ( after ) ;
484
+ }
485
+ return after ( returnValue ) ;
459
486
}
460
487
} ;
461
488
0 commit comments