diff --git a/.changeset/floppy-mails-sing.md b/.changeset/floppy-mails-sing.md new file mode 100644 index 000000000000..031fe5026de8 --- /dev/null +++ b/.changeset/floppy-mails-sing.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: streamed promise not resolving when another load function returns a fast resolving promise diff --git a/packages/kit/src/utils/streaming.js b/packages/kit/src/utils/streaming.js index 449507ac6695..6595405ea0ad 100644 --- a/packages/kit/src/utils/streaming.js +++ b/packages/kit/src/utils/streaming.js @@ -1,18 +1,4 @@ -/** - * @returns {import('types').Deferred & { promise: Promise }}} - */ -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 @@ -23,9 +9,11 @@ function defer() { * }} */ export function create_async_iterator() { - let count = 0; + let resolved = -1; + let returned = -1; - const deferred = [defer()]; + /** @type {import('./promise.js').PromiseWithResolvers[]} */ + const deferred = []; return { iterate: (transform = (x) => x) => { @@ -33,32 +21,20 @@ export function create_async_iterator() { [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); }); } }; diff --git a/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.server.js b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.server.js new file mode 100644 index 000000000000..add2dafa0979 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.server.js @@ -0,0 +1,3 @@ +export function load() { + return { fast: Promise.resolve('fast') }; +} diff --git a/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.svelte b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.svelte new file mode 100644 index 000000000000..40a71c4b9a56 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+layout.svelte @@ -0,0 +1,12 @@ + + +{#await data.fast} +

Loading fast

+{:then result} +

{result}

+{/await} + + diff --git a/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.server.js b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.server.js new file mode 100644 index 000000000000..476a5ee63044 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.server.js @@ -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 }; +} diff --git a/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.svelte b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.svelte new file mode 100644 index 000000000000..9179468948b4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/streaming/server/fast-n-slow/+page.svelte @@ -0,0 +1,12 @@ + + +

{data.ssrd}

+ +{#await data.streamed} +

Loading streamed

+{:then result} +

{result}

+{/await} diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index fec98e4a214e..3bd657ed2a9e 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -1308,6 +1308,16 @@ test.describe('Streaming', () => { 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');