diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f8b50324f..2afd2b2706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Rework SSR store handling to subscribe and unsubscribe as in DOM mode ([#3375](https://github.com/sveltejs/svelte/issues/3375), [#3582](https://github.com/sveltejs/svelte/issues/3582), [#3636](https://github.com/sveltejs/svelte/issues/3636)) * Fix error when removing elements that are already transitioning out ([#5789](https://github.com/sveltejs/svelte/issues/5789), [#5808](https://github.com/sveltejs/svelte/issues/5808)) +* Fix duplicate content race condition with `{#await}` blocks and out transitions ([#5815](https://github.com/sveltejs/svelte/issues/5815)) ## 3.31.1 diff --git a/src/runtime/internal/await_block.ts b/src/runtime/internal/await_block.ts index 4b7ca6fd21..b93f216b32 100644 --- a/src/runtime/internal/await_block.ts +++ b/src/runtime/internal/await_block.ts @@ -28,7 +28,9 @@ export function handle_promise(promise, info) { if (i !== index && block) { group_outros(); transition_out(block, 1, 1, () => { - info.blocks[i] = null; + if (info.blocks[i] === block) { + info.blocks[i] = null; + } }); check_outros(); } diff --git a/test/runtime/samples/transition-js-await-block-outros/_config.js b/test/runtime/samples/transition-js-await-block-outros/_config.js new file mode 100644 index 0000000000..7233e3f67f --- /dev/null +++ b/test/runtime/samples/transition-js-await-block-outros/_config.js @@ -0,0 +1,174 @@ +let fulfil; + +export default { + props: { + promise: new Promise((f) => { + fulfil = f; + }) + }, + intro: true, + + async test({ assert, target, component, raf }) { + assert.htmlEqual(target.innerHTML, '
loading...
'); + + let time = 0; + + raf.tick(time += 50); + assert.htmlEqual(target.innerHTML, 'loading...
'); + + await fulfil(42); + + assert.htmlEqual(target.innerHTML, ` +42
+loading...
+ `); + + // see the transition 30% complete + raf.tick(time += 30); + assert.htmlEqual(target.innerHTML, ` +42
+loading...
+ `); + + // completely transition in the {:then} block + raf.tick(time += 70); + assert.htmlEqual(target.innerHTML, ` +42
+ `); + + // update promise #1 + component.promise = new Promise((f) => { + fulfil = f; + }); + await Promise.resolve(); + + assert.htmlEqual(target.innerHTML, ` +42
+loading...
+ `); + + raf.tick(time += 100); + + assert.htmlEqual(target.innerHTML, ` +loading...
+ `); + + await fulfil(43); + assert.htmlEqual(target.innerHTML, ` +loading...
+43
+ `); + + raf.tick(time += 100); + assert.htmlEqual(target.innerHTML, ` +43
+ `); + + // update promise #2 + component.promise = new Promise((f) => { + fulfil = f; + }); + await Promise.resolve(); + + assert.htmlEqual(target.innerHTML, ` +43
+loading...
+ `); + + raf.tick(time += 50); + + assert.htmlEqual(target.innerHTML, ` +43
+loading...
+ `); + + await fulfil(44); + assert.htmlEqual(target.innerHTML, ` +43
+loading...
+44
+ `); + + raf.tick(time += 100); + assert.htmlEqual(target.innerHTML, ` +44
+ `); + + // update promise #3 - quick succession + component.promise = new Promise((f) => { + fulfil = f; + }); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+ `); + + raf.tick(time += 40); + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+ `); + + await fulfil(45); + + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+45
+ `); + + raf.tick(time += 20); + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+45
+ `); + + component.promise = new Promise((f) => { + fulfil = f; + }); + await Promise.resolve(); + + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+45
+loading...
+ `); + + raf.tick(time += 10); + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+45
+loading...
+ `); + + await fulfil(46); + + assert.htmlEqual(target.innerHTML, ` +44
+loading...
+45
+loading...
+46
+ `); + + raf.tick(time += 10); + assert.htmlEqual(target.innerHTML, ` +44
+46
+ `); + + raf.tick(time += 20); + assert.htmlEqual(target.innerHTML, ` +46
+ `); + + raf.tick(time += 70); + assert.htmlEqual(target.innerHTML, ` +46
+ `); + } +}; diff --git a/test/runtime/samples/transition-js-await-block-outros/main.svelte b/test/runtime/samples/transition-js-await-block-outros/main.svelte new file mode 100644 index 0000000000..6060b7672b --- /dev/null +++ b/test/runtime/samples/transition-js-await-block-outros/main.svelte @@ -0,0 +1,20 @@ + + +{#await promise} +loading...
+{:then value} +{value}
+{:catch error} +{error.message}
+{/await} \ No newline at end of file