You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

疑问

请问我哪里处理错误了?


问题原因与修复方案

错误原因

  1. forEach不支持异步等待:res.items.forEach(async (itemRef) => {...})中,forEach会立即遍历所有元素,不会等待每个异步函数执行完成,所以外层的await listAll(...)只会等listAll完成,不会等所有getDownloadURL操作结束。
  2. 混用await与then:代码同时使用await和.then(),导致异步流程控制混乱,进一步加剧了执行顺序问题。
  3. 数组赋值方式不安全:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 21:46:09