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 中。
// 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
组件)
// 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}
我尝试在 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 中。
// 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
组件)
// 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}