Skip to content

Commit a688c83

Browse files
committed
feat: schedule resource task
1 parent b089e99 commit a688c83

File tree

4 files changed

+48
-44
lines changed

4 files changed

+48
-44
lines changed

packages/qwik/src/core/shared/scheduler.ts

+25-28
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,14 @@ import { isSignal, type Signal } from '../signal/signal.public';
9696
import type { TargetType } from '../signal/store';
9797
import type { ISsrNode } from '../ssr/ssr-types';
9898
import { runResource, type ResourceDescriptor } from '../use/use-resource';
99-
import { Task, TaskFlags, cleanupTask, runTask, type TaskFn } from '../use/use-task';
99+
import {
100+
Task,
101+
TaskFlags,
102+
cleanupTask,
103+
runTask,
104+
type DescriptorBase,
105+
type TaskFn,
106+
} from '../use/use-task';
100107
import { executeComponent } from './component-execution';
101108
import type { OnRenderFn } from './component.public';
102109
import { assertEqual, assertFalse } from './error/assert';
@@ -124,7 +131,6 @@ export const enum ChoreType {
124131
/** Ensure that the QRL promise is resolved before processing next chores in the queue */
125132
QRL_RESOLVE /* ********************** */ = 0b0000_0001,
126133
RUN_QRL,
127-
RESOURCE,
128134
TASK,
129135
NODE_DIFF,
130136
NODE_PROP,
@@ -208,10 +214,7 @@ export const createScheduler = (
208214
host: HostElement | null,
209215
target: Signal
210216
): ValueOrPromise<void>;
211-
function schedule(
212-
type: ChoreType.TASK | ChoreType.VISIBLE | ChoreType.RESOURCE,
213-
task: Task
214-
): ValueOrPromise<void>;
217+
function schedule(type: ChoreType.TASK | ChoreType.VISIBLE, task: Task): ValueOrPromise<void>;
215218
function schedule(
216219
type: ChoreType.RUN_QRL,
217220
host: HostElement,
@@ -250,10 +253,7 @@ export const createScheduler = (
250253
const runLater: boolean =
251254
type !== ChoreType.WAIT_FOR_ALL && !isComponentSsr && type !== ChoreType.RUN_QRL;
252255
const isTask =
253-
type === ChoreType.TASK ||
254-
type === ChoreType.VISIBLE ||
255-
type === ChoreType.RESOURCE ||
256-
type === ChoreType.CLEANUP_VISIBLE;
256+
type === ChoreType.TASK || type === ChoreType.VISIBLE || type === ChoreType.CLEANUP_VISIBLE;
257257
const isClientOnly =
258258
type === ChoreType.JOURNAL_FLUSH ||
259259
type === ChoreType.NODE_DIFF ||
@@ -399,22 +399,6 @@ export const createScheduler = (
399399
);
400400
}
401401
break;
402-
case ChoreType.RESOURCE:
403-
{
404-
const result = runResource(
405-
chore.$payload$ as ResourceDescriptor<TaskFn>,
406-
container,
407-
host
408-
);
409-
// Don't await the return value of the resource, because async resources should not be awaited.
410-
// The reason for this is that we should be able to update for example a node with loading
411-
// text. If we await the resource, the loading text will not be displayed until the resource
412-
// is loaded.
413-
// Awaiting on the client also causes a deadlock.
414-
// In any case, the resource will never throw.
415-
returnValue = isServer ? result : null;
416-
}
417-
break;
418402
case ChoreType.RUN_QRL:
419403
{
420404
const fn = (chore.$target$ as QRLInternal<(...args: unknown[]) => unknown>).getFn();
@@ -444,7 +428,21 @@ export const createScheduler = (
444428
break;
445429
case ChoreType.TASK:
446430
case ChoreType.VISIBLE:
447-
returnValue = runTask(chore.$payload$ as Task<TaskFn, TaskFn>, container, host);
431+
{
432+
const payload = chore.$payload$ as DescriptorBase;
433+
if (payload.$flags$ & TaskFlags.RESOURCE) {
434+
const result = runResource(payload as ResourceDescriptor<TaskFn>, container, host);
435+
// Don't await the return value of the resource, because async resources should not be awaited.
436+
// The reason for this is that we should be able to update for example a node with loading
437+
// text. If we await the resource, the loading text will not be displayed until the resource
438+
// is loaded.
439+
// Awaiting on the client also causes a deadlock.
440+
// In any case, the resource will never throw.
441+
returnValue = isServer ? result : null;
442+
} else {
443+
returnValue = runTask(payload as Task<TaskFn, TaskFn>, container, host);
444+
}
445+
}
448446
break;
449447
case ChoreType.CLEANUP_VISIBLE:
450448
{
@@ -677,7 +675,6 @@ function debugChoreTypeToString(type: ChoreType): string {
677675
{
678676
[ChoreType.QRL_RESOLVE]: 'QRL_RESOLVE',
679677
[ChoreType.RUN_QRL]: 'RUN_QRL',
680-
[ChoreType.RESOURCE]: 'RESOURCE',
681678
[ChoreType.TASK]: 'TASK',
682679
[ChoreType.NODE_DIFF]: 'NODE_DIFF',
683680
[ChoreType.NODE_PROP]: 'NODE_PROP',

packages/qwik/src/core/signal/signal.ts

-2
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,6 @@ export const triggerEffects = (
331331
let choreType = ChoreType.TASK;
332332
if (effect.$flags$ & TaskFlags.VISIBLE_TASK) {
333333
choreType = ChoreType.VISIBLE;
334-
} else if (effect.$flags$ & TaskFlags.RESOURCE) {
335-
choreType = ChoreType.RESOURCE;
336334
}
337335
container.$scheduler$(choreType, effect);
338336
} else if (effect instanceof Signal) {

packages/qwik/src/core/use/use-resource.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { clearSubscriberEffectDependencies } from '../signal/signal-subscriber';
1717
import { ResourceEvent } from '../shared/utils/markers';
1818
import { assertDefined } from '../shared/error/assert';
1919
import { noSerialize } from '../shared/utils/serialize-utils';
20+
import { ChoreType } from '../shared/scheduler';
2021

2122
const DEBUG: boolean = false;
2223

@@ -104,7 +105,7 @@ export const useResourceQrl = <T>(
104105
resource,
105106
null
106107
) as ResourceDescriptor<any>;
107-
runResource(task, container, iCtx.$hostElement$);
108+
container.$scheduler$(ChoreType.TASK, task);
108109
set(resource);
109110

110111
return resource;
@@ -183,7 +184,7 @@ export const Resource = <T>(props: ResourceProps<T>): JSXOutput => {
183184

184185
function getResourceValueAsPromise<T>(props: ResourceProps<T>): Promise<JSXOutput> | JSXOutput {
185186
const resource = props.value as ResourceReturnInternal<T> | Promise<T> | Signal<T>;
186-
if (isResourceReturn(resource)) {
187+
if (isResourceReturn(resource) && resource.value) {
187188
const isBrowser = !isServerPlatform();
188189
if (isBrowser) {
189190
// create a subscription for the resource._state changes

starters/e2e/e2e.resource.e2e.ts

+20-12
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ test.describe("resource", () => {
1515
const resource1 = page.locator(".resource1");
1616
const logs = page.locator(".logs");
1717
const increment = page.locator("button.increment");
18-
let logsContent =
19-
"[WATCH] 1 before\n[WATCH] 1 after\n[WATCH] 2 before\n[WATCH] 2 after\n[RESOURCE] 1 before\n";
20-
// TODO: server promise streaming is not supported, so for now we can't test this correctly
21-
logsContent += "[RESOURCE] 1 after\n\n";
18+
let logsContent = "";
19+
// execute first task
20+
logsContent += "[WATCH] 1 before\n[WATCH] 1 after\n";
21+
// execute second task
22+
logsContent += "[WATCH] 2 before\n[WATCH] 2 after\n";
23+
// execute the resource
24+
logsContent += "[RESOURCE] 1 before\n[RESOURCE] 1 after\n\n";
2225
await expect(resource1).toHaveText("resource 1 is 80");
2326
// await expect(resource2).toHaveText('resource 2 is 160');
2427
await expect(logs).toHaveText(logsContent);
@@ -27,24 +30,30 @@ test.describe("resource", () => {
2730
await increment.click();
2831

2932
await expect(resource1).toHaveText("loading resource 1...");
30-
logsContent +=
31-
"[WATCH] 1 before\n[WATCH] 1 after\n[WATCH] 2 before\n[WATCH] 2 after\n[RESOURCE] 1 before\n";
33+
// execute first task
34+
logsContent += "[WATCH] 1 before\n[WATCH] 1 after\n";
35+
// execute second task
36+
logsContent += "[WATCH] 2 before\n[WATCH] 2 after\n";
37+
// rexecute the resource
38+
logsContent += "[RESOURCE] 1 before\n[RESOURCE] 1 after\n\n";
3239
// await expect(resource2).toHaveText('loading resource 2...');
3340
await expect(logs).toHaveText(logsContent);
3441

3542
await expect(resource1).toHaveText("resource 1 is 88");
36-
logsContent += "[RESOURCE] 1 after\n\n";
3743
// await expect(resource2).toHaveText('resource 2 is 176');
3844
await expect(logs).toHaveText(logsContent);
3945
});
4046

4147
test("should track subscriptions", async ({ page }) => {
4248
const resource1 = page.locator(".resource1");
4349
const logs = page.locator(".logs");
44-
let logsContent =
45-
"[WATCH] 1 before\n[WATCH] 1 after\n[WATCH] 2 before\n[WATCH] 2 after\n[RESOURCE] 1 before\n";
46-
// TODO: server promise streaming is not supported, so for now we can't test this correctly
47-
logsContent += "[RESOURCE] 1 after\n\n";
50+
let logsContent = "";
51+
// execute first task
52+
logsContent += "[WATCH] 1 before\n[WATCH] 1 after\n";
53+
// execute second task
54+
logsContent += "[WATCH] 2 before\n[WATCH] 2 after\n";
55+
// execute the resource
56+
logsContent += "[RESOURCE] 1 before\n[RESOURCE] 1 after\n\n";
4857
await expect(resource1).toHaveText("resource 1 is 80");
4958
await expect(logs).toHaveText(logsContent);
5059

@@ -54,7 +63,6 @@ test.describe("resource", () => {
5463
await countBtn.click();
5564
await expect(countBtn).toHaveText("count is 1");
5665

57-
// logsContent += "[RESOURCE] 1 after\n\n";
5866
await expect(logs).toHaveText(logsContent);
5967

6068
await countBtn.click();

0 commit comments

Comments
 (0)