From d9742ef39f1f50d71dc3c59c16446b534d749267 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 5 Jan 2021 05:19:24 +0800 Subject: [PATCH] fix await outro race condition (#5850) --- CHANGELOG.md | 1 + src/runtime/internal/await_block.ts | 4 +- .../_config.js | 174 ++++++++++++++++++ .../main.svelte | 20 ++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/transition-js-await-block-outros/_config.js create mode 100644 test/runtime/samples/transition-js-await-block-outros/main.svelte 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