Svelte 中的每个异步

Async each in Svelte

我尝试在 svelte 中使用异步 #each 循环,发现它无论如何都会同步运行,使用这样的异步等待函数:

{#each items as item (item.id)}
    {#await render(item) then source}
        <Canvas {source} />
    {:catch}
        <p>error</p>
    {/await}
{/each}

发现无法在 svelte 中使用异步组件,因为 onmount 不能像这样异步使用:

{#each items as item (item.id)}
    <Canvas {item} />
{/each}

这个问题有解决办法吗?

根据您的评论,您似乎在使用异步更新数组中的元素时遇到了问题。这是一个使用异步函数在 onMount 中初始化,然后使用 immutable 标志重新渲染以仅更新那些已更改的元素的示例。此示例将所有承诺处理放在脚本中而不是放在 jsx 中。

REPL

// App.svelte
<svelte:options immutable={false} />

<script>
    import Item from "./Item.svelte";
    import { onMount } from "svelte";

    let array;

    const sleep = async (ms) => await new Promise((r) => setTimeout(r, ms));

    async function render(id) {
        await sleep(800);
        array = array.map((item) => {
            if (item.id === id) {
                return {
                    ...item,
                    render: item.render + 1,
                };
            }
            return item;
        });
    }

    onMount(() => {
        async function init() {
            array = await Promise.all(
                [...Array.from({ length: 5 }).keys()].map((i) =>
                    Promise.resolve({
                        id: i + 1,
                        name: `item_${i + 1}`,
                        render: 0,
                    })
                )
            );
        }

        init();

        return () => console.log("destroyed");
    });
</script>

{#if array && array.length}
    {#each array as item (item.id)}
        <Item {item} on:click={() => render(item.id)} />
    {/each}
{/if}
// ./Item.svelte
<svelte:options immutable={true} />

<script>
    import { afterUpdate } from "svelte";
    
    export let item;
</script>

<div>
    <p>
        {item.name} [render: {item.render}]
    </p>
    <button on:click>render</button>
</div>

但您也可以合并一个 {#await} 块来处理 jsx 中的一系列承诺。 (它使用与上面相同的 Item 组件)

REPL

// App.svelte
<svelte:options immutable={false} />

<script>
    import Item from "./Item.svelte";
    import { onMount } from "svelte";

    let array;

    async function render(id) {
        array = await Promise.all(
            array.map(async (promiseItem) => {
                if (promiseItem.id === id) {
                    const _item = await promiseItem.item;
                    return {
                        id: promiseItem.id,
                        item: new Promise((r) =>
                            setTimeout(() => r({ ..._item, render: _item.render + 1,}), 800)
                        ),
                    };
                }
                return promiseItem;
            })
        );
    }

    onMount(() => {
        async function init() {
            array = [...Array.from({ length: 5 }).keys()].map((i) => ({
                id: i,
                item: Promise.resolve({
                    id: i + 1,
                    name: `item_${i + 1}`,
                    render: 0,
                }),
            }));
        }

        init();

        return () => console.log("destroyed");
    });
</script>

{#if array && array.length}
    {#each array as { id, item: promise } (id)}
        {#await promise}
            <p>...rendering</p>
        {:then item}
            <Item {item} on:click={() => render(item.id)} />
        {/await}
    {/each}
{/if}

原回答

您还没有真正弄清楚什么不起作用。您 可以 运行 在 onMount 中异步调用并在 {#await} 块中等待承诺。

这是一个人为的 REPL,它构建了一个 promise 数组,使用 Promise.all 来解析它们并将生成的 promise 分配给要呈现的变量。

<script>
    import { onMount } from "svelte";

    let promise;

    async function render(i) {
        return {
            id: i + 1,
            name: `item_${i + 1}`,
        };
    }

    onMount(() => {
        const array = [...Array.from({length: 5}).keys()];
        promise = Promise.all(array.map((i) => render(i)));

        return () => console.log("destroyed");
    });
</script>

{#await promise}
    <p>...rendering</p>
{:then array}
    {#if array && array.length}
        {#each array as item (item.id)}
            <p>
                {item.name}
            </p>
        {/each}
    {/if}
{:catch error}
    <p>oh dear.</p>
{/await}