Skip to content

Commit fc6017c

Browse files
authored
fix: streamed promise not resolving (#14753)
* test * fix * format * changeset * dont' hang * DRY * simpler version
1 parent c9b31d9 commit fc6017c

File tree

7 files changed

+58
-35
lines changed

7 files changed

+58
-35
lines changed

.changeset/floppy-mails-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: streamed promise not resolving when another load function returns a fast resolving promise

packages/kit/src/utils/streaming.js

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,4 @@
1-
/**
2-
* @returns {import('types').Deferred & { promise: Promise<any> }}}
3-
*/
4-
function defer() {
5-
let fulfil;
6-
let reject;
7-
8-
const promise = new Promise((f, r) => {
9-
fulfil = f;
10-
reject = r;
11-
});
12-
13-
// @ts-expect-error
14-
return { promise, fulfil, reject };
15-
}
1+
import { with_resolvers } from './promise.js';
162

173
/**
184
* Create an async iterator and a function to push values into it
@@ -23,42 +9,32 @@ function defer() {
239
* }}
2410
*/
2511
export function create_async_iterator() {
26-
let count = 0;
12+
let resolved = -1;
13+
let returned = -1;
2714

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

3018
return {
3119
iterate: (transform = (x) => x) => {
3220
return {
3321
[Symbol.asyncIterator]() {
3422
return {
3523
next: async () => {
36-
const next = await deferred[0].promise;
24+
const next = deferred[++returned];
25+
if (!next) return { value: null, done: true };
3726

38-
if (!next.done) {
39-
deferred.shift();
40-
return { value: transform(next.value), done: false };
41-
}
42-
43-
return next;
27+
const value = await next.promise;
28+
return { value: transform(value), done: false };
4429
}
4530
};
4631
}
4732
};
4833
},
4934
add: (promise) => {
50-
count += 1;
51-
35+
deferred.push(with_resolvers());
5236
void promise.then((value) => {
53-
deferred[deferred.length - 1].fulfil({
54-
value,
55-
done: false
56-
});
57-
deferred.push(defer());
58-
59-
if (--count === 0) {
60-
deferred[deferred.length - 1].fulfil({ done: true });
61-
}
37+
deferred[++resolved].resolve(value);
6238
});
6339
}
6440
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function load() {
2+
return { fast: Promise.resolve('fast') };
3+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
/** @type {import('./$types').LayoutData} */
3+
export let data;
4+
</script>
5+
6+
{#await data.fast}
7+
<p class="loadingfast">Loading fast</p>
8+
{:then result}
9+
<p class="fast">{result}</p>
10+
{/await}
11+
12+
<slot />
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export async function load() {
2+
const ssrd = await new Promise((r) => setTimeout(() => r('ssrd'), 100));
3+
const streamed = new Promise((r) => setTimeout(() => r('streamed'), 200));
4+
return { ssrd, streamed };
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
/** @type {import('./$types').PageData} */
3+
export let data;
4+
</script>
5+
6+
<p class="ssrd">{data.ssrd}</p>
7+
8+
{#await data.streamed}
9+
<p class="loadingstreamed">Loading streamed</p>
10+
{:then result}
11+
<p class="streamed">{result}</p>
12+
{/await}

packages/kit/test/apps/basics/test/client.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,16 @@ test.describe('Streaming', () => {
13081308
expect(page.locator('p.loadingfail')).toBeHidden();
13091309
});
13101310

1311+
test('Works with a fast and a slow server load functions which (direct hit)', async ({
1312+
page
1313+
}) => {
1314+
await page.goto('/streaming/server/fast-n-slow');
1315+
1316+
expect(await page.locator('p.ssrd').textContent()).toBe('ssrd');
1317+
await expect(page.locator('p.fast')).toHaveText('fast');
1318+
await expect(page.locator('p.streamed')).toHaveText('streamed');
1319+
});
1320+
13111321
test('Catches fetch errors from server load functions (direct hit)', async ({ page }) => {
13121322
page.goto('/streaming/server-error');
13131323
await expect(page.locator('p.eager')).toHaveText('eager');

0 commit comments

Comments
 (0)