Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/floppy-mails-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: streamed promise not resolving when another load function returns a fast resolving promise
46 changes: 11 additions & 35 deletions packages/kit/src/utils/streaming.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
/**
* @returns {import('types').Deferred & { promise: Promise<any> }}}
*/
function defer() {
let fulfil;
let reject;

const promise = new Promise((f, r) => {
fulfil = f;
reject = r;
});

// @ts-expect-error
return { promise, fulfil, reject };
}
import { with_resolvers } from './promise.js';

/**
* Create an async iterator and a function to push values into it
Expand All @@ -23,42 +9,32 @@ function defer() {
* }}
*/
export function create_async_iterator() {
let count = 0;
let resolved = -1;
let returned = -1;

const deferred = [defer()];
/** @type {import('./promise.js').PromiseWithResolvers<T>[]} */
const deferred = [];

return {
iterate: (transform = (x) => x) => {
return {
[Symbol.asyncIterator]() {
return {
next: async () => {
const next = await deferred[0].promise;
const next = deferred[++returned];
if (!next) return { value: null, done: true };

if (!next.done) {
deferred.shift();
return { value: transform(next.value), done: false };
}

return next;
const value = await next.promise;
return { value: transform(value), done: false };
}
};
}
};
},
add: (promise) => {
count += 1;

deferred.push(with_resolvers());
void promise.then((value) => {
deferred[deferred.length - 1].fulfil({
value,
done: false
});
deferred.push(defer());

if (--count === 0) {
deferred[deferred.length - 1].fulfil({ done: true });
}
deferred[++resolved].resolve(value);
});
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function load() {
return { fast: Promise.resolve('fast') };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>

{#await data.fast}
<p class="loadingfast">Loading fast</p>
{:then result}
<p class="fast">{result}</p>
{/await}

<slot />
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function load() {
const ssrd = await new Promise((r) => setTimeout(() => r('ssrd'), 100));
const streamed = new Promise((r) => setTimeout(() => r('streamed'), 200));
return { ssrd, streamed };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>

<p class="ssrd">{data.ssrd}</p>

{#await data.streamed}
<p class="loadingstreamed">Loading streamed</p>
{:then result}
<p class="streamed">{result}</p>
{/await}
10 changes: 10 additions & 0 deletions packages/kit/test/apps/basics/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@
});

test.describe('data-sveltekit attributes', () => {
test('data-sveltekit-preload-code', async ({ page }) => {

Check warning on line 867 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit-server-side-route-resolution (build)

flaky test: data-sveltekit-preload-code

retries: 2
/** @type {string[]} */
const responses = [];

Expand Down Expand Up @@ -1308,6 +1308,16 @@
expect(page.locator('p.loadingfail')).toBeHidden();
});

test('Works with a fast and a slow server load functions which (direct hit)', async ({
page
}) => {
await page.goto('/streaming/server/fast-n-slow');

expect(await page.locator('p.ssrd').textContent()).toBe('ssrd');
await expect(page.locator('p.fast')).toHaveText('fast');
await expect(page.locator('p.streamed')).toHaveText('streamed');
});

test('Catches fetch errors from server load functions (direct hit)', async ({ page }) => {
page.goto('/streaming/server-error');
await expect(page.locator('p.eager')).toHaveText('eager');
Expand Down
Loading