Svelte 5 onMount中await/async未等待问题排查求助
问题描述
我开发了一个基于Svelte 5的Firebase应用,使用的版本如下:
- Svelte:
5.17.5 - @sveltejs/kit:
2.15.3 - firebase:
11.2.0 - vite:
6.0.7
在onMount的异步函数中调用Firebase Storage的listAll和getDownloadURL方法时,虽然使用了await,但代码并未等待所有异步操作完成就执行了后续的console.log,控制台输出顺序不符合预期。
相关代码
导入部分:
import { getDownloadURL, getStorage, listAll, ref } from 'firebase/storage'; import { page } from '$app/state'; import { getZStorageI, zFirestore, zlataTrinket_kategorija, generateKeyword, type ZlataTrinket } from '$lib/common';
核心逻辑:
let pictureUrl: Array<string> = $state([]); onMount(async () => { const _id = page.url.searchParams.get('id'); const storage = getStorage(); const listRef = ref(storage, getZStorageI(_id)); let i = 0; await listAll(listRef).then(async (res) => { res.items.forEach(async (itemRef) => { await getDownloadURL(itemRef).then(async (url) => { console.log(url); pictureUrl[i++] = url; }); }); }); console.log("It should be after?"); });
控制台输出
It should be after? +page.svelte:44 https://firebasestorage.googleapis.com/v0/b/zlata-67787.firebasestorage.app/o/zStorage%2F400_499%2F463%2F2.jpg?alt=media&token=fd48f7c8-d90d-4841-91e9-7fd3ca88780a +page.svelte:44 https://firebasestorage.googleapis.com/v0/b/zlata-67787.firebasestorage.app/o/zStorage%2F400_499%2F463%2F4.jpg?alt=media&token=2329c929-75b8-4251-a02e-82b31b2febcb +page.svelte:44 https://firebasestorage.googleapis.com/v0/b/zlata-67787.firebasestorage.app/o/zStorage%2F400_499%2F463%2F1.jpg?alt=media&token=5fbdb696-977b-498d-928a-3280843e4748 +page.svelte:44 https://firebasestorage.googleapis.com/v0/b/zlata-67787.firebasestorage.app/o/zStorage%2F400_49
疑问
请问我哪里处理错误了?
问题原因与修复方案
错误原因
forEach不支持异步等待:res.items.forEach(async (itemRef) => {...})中,forEach会立即遍历所有元素,不会等待每个异步函数执行完成,所以外层的await listAll(...)只会等listAll完成,不会等所有getDownloadURL操作结束。- 混用
await与then:代码同时使用await和.then(),导致异步流程控制混乱,进一步加剧了执行顺序问题。 - 数组赋值方式不安全:使用
pictureUrl[i++] = url的方式修改数组,在异步场景下可能导致索引混乱,而且Svelte的$state对数组的索引赋值跟踪可能不够灵敏。
修复后的代码
let pictureUrl: Array<string> = $state([]); onMount(async () => { const _id = page.url.searchParams.get('id'); if (!_id) return; // 增加空值判断,避免后续出错 const storage = getStorage(); const listRef = ref(storage, getZStorageI(_id)); // 先获取文件列表 const res = await listAll(listRef); // 用map生成所有getDownloadURL的Promise,再用Promise.all等待全部完成 const urls = await Promise.all( res.items.map(itemRef => getDownloadURL(itemRef)) ); // 直接替换数组,让Svelte更好地跟踪状态变化 pictureUrl = urls; // 此时所有下载已完成,再执行后续逻辑 console.log("It should be after?"); urls.forEach(url => console.log(url)); });
修复说明
- 使用
Promise.all配合map:将每个getDownloadURL调用转为Promise,通过Promise.all等待所有异步操作完成,确保后续代码在所有URL获取后执行。 - 避免混用
await与then:全程使用await进行异步流程控制,代码更清晰易读。 - 直接替换数组:使用
pictureUrl = urls而非索引赋值,既避免索引混乱,也让Svelte的状态系统能准确感知数组变化,触发视图更新。 - 增加空值判断:提前处理
_id为空的情况,避免无效的Storage操作。
内容的提问来源于stack exchange,提问作者Hrvoje
相关产品推荐
相关产品推荐

