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

Firebase RTDB v9中await查询异常问题咨询及解决方案

Firebase RTDB v8.2升级v9.1后await查询异常问题解析

问题现象

当前使用Firebase RTDB 8.2版本,升级至9.1版本后,应用中使用await的查询出现严重异常:await不再等待完整数据加载完成,仅获取第一条数据就继续执行后续逻辑,导致计算结果错误。

测试代码中的核心逻辑是点击按钮触发_submit()方法,从数据库加载商品列表并计算数量总和:

Future<void> _submit() async {
  List<ItemModel> itemsList = [];
  itemsList = await loadItems();
  int _sumQty = itemsList.fold(0, (a, b) => a + b.quantity!);
  print('$_sumQty was the sum of quantities');
}

v8.2.0版本(Web端)预期结果

{item: iPhone, quantity: 5} {item: Samsung, quantity: 10} {item: LG, quantity: 15} {item: NTC, quantity: 20} {item: Zenith, quantity: 25} {item: Sony, quantity: 30} {item: JVC, quantity: 40}

7 Items were loaded

145 was the sum of quantities

v9.1.4版本(Web端)错误结果

{item: iPhone, quantity: 5}

1 Items were loaded

5 was the sum of quantities

{item: Samsung, quantity: 10} {item: LG, quantity: 15} {item: NTC, quantity: 20} {item: Zenith, quantity: 25} {item: Sony, quantity: 30} {item: JVC, quantity: 40}

该问题Web端100%复现,移动端(Android)偶发。

核心疑问

  1. Firebase新版本中await行为变更的根源是什么?
  2. 若迁移至最新版本,需对代码做出哪些修改以避免此类查询异常?

附件:Firebase 8.2.0完整测试代码

import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_database/ui/firebase_list.dart';
import 'package:flutter/material.dart';
 
class Prueba extends StatefulWidget {
 const Prueba({Key? key}) : super(key: key);
 
 @override
 State<Prueba> createState() => _PruebaState();
}
 
class ItemModel {
   String? item;
   int? quantity;
 
   ItemModel({
       this.item,
       this.quantity,
   });
}
 
class _PruebaState extends State<Prueba> {

@override
 void dispose() {
   super.dispose();
 }

@override
void initState() {
 super.initState(); 
}
 
 @override
 Widget build(BuildContext context) {
 
   return Scaffold(
     appBar: AppBar(
       title: const Text('Async InitState Test'),
     ),
     body: createButton('Query'),
 
   );
 }
 
   Widget createButton(String texto) {
   return Container(
     width: double.infinity,
     padding: const EdgeInsets.only(top:30.0, left:10.0, right:10.0),
     child: ElevatedButton(
       onPressed: _submit,
       child: Text(texto, style: Theme.of(context).textTheme.headline6),
     ),
   );
 }
 
 Future<void> _submit() async {
   List<ItemModel> itemsList = [];
   itemsList = await loadItems();
   int _sumQty = itemsList.fold(0, (a, b) => a + b.quantity!);
   print('$_sumQty was the sum of quantities');
 }
 
 
 Future<List<ItemModel>> loadItems() async {
 
   final List<ItemModel> items = [];
 
   String path = '/items';
   Query resp = FirebaseDatabase.instance.reference().child(path);
 
   FirebaseList(
     query: resp,
     onChildAdded: (i, element) {
       print(element.value);
       ItemModel temp = ItemModel()
         ..item = element.value["item"]
         ..quantity = element.value["quantity"];
       items.add(temp);
     },
     onError: (e) => print(e.message)
   );
 
   await resp.once().then((snapshot) {
     print("${items.length} Items were loaded");
   });
 
   return items;
 }
}

适配Firebase 9.1.4的原始修改(存在问题)

Future<List<ItemModel>> loadItems() async {
  final List<ItemModel> items = []; 
  String path = '/empresas/-ME9qZY5k8RxpCymZlV2/items';
  Query resp = FirebaseDatabase.instance.ref().child(path);

  FirebaseList(
    query: resp,
    onChildAdded: (i, element) {
      print(element.value);
      Map<dynamic, dynamic> map = element.value as dynamic; 
      ItemModel temp = ItemModel()
        ..item = map["item"]
        ..quantity = map["quantity"];
      items.add(temp);
    },
    onError: (e) => print(e.message)
  );

  await resp.once().then((snapshot) {
    print("${items.length} Items were loaded");
  });

  return items;
}

问题解答

1. 行为变更的根源

Firebase RTDB v9+采用模块化API重构,同时调整了一次性读取(once/get)与实时监听(onChildAdded)的执行逻辑绑定关系:

  • 在v8版本中,once()方法会隐式等待FirebaseList的所有onChildAdded回调执行完毕后,才完成Future;
  • v9+版本中,once()(或替代的get())仅保证获取到数据快照本身,不再等待FirebaseList的异步回调触发——FirebaseList作为UI组件,其回调是独立的异步流程,和一次性读取的Future完成时机不再绑定。

这就导致v9+中await resp.once()完成时,onChildAdded可能只触发了第一条数据的回调,后续数据还在异步加载,最终返回的列表不完整。

2. 代码修改方案

你的核心问题是混用了UI组件FirebaseList和一次性数据读取逻辑,正确的做法是直接通过数据快照解析完整数据,而非依赖FirebaseList的回调:

修改后的loadItems方法

Future<List<ItemModel>> loadItems() async {
  final List<ItemModel> items = []; 
  String path = '/empresas/-ME9qZY5k8RxpCymZlV2/items';
  // 使用v9+推荐的get()方法替代once()
  final snapshot = await FirebaseDatabase.instance.ref().child(path).get();

  if (snapshot.exists) {
    // 遍历快照所有子节点,一次性解析完整数据
    snapshot.children.forEach((child) {
      Map<dynamic, dynamic> map = child.value as dynamic;
      print(map);
      ItemModel temp = ItemModel()
        ..item = map["item"]
        ..quantity = map["quantity"];
      items.add(temp);
    });
  }

  print("${items.length} Items were loaded");
  return items;
}

修改说明

  • 移除FirebaseList:它是用于UI实时渲染的组件,不适合在纯数据获取的异步方法中使用;
  • 替换once()为get():这是v9+模块化API的标准一次性读取方法,功能一致但更符合新API规范;
  • 直接遍历快照children:从完整数据快照中一次性解析所有子节点,确保返回的列表是完整的,避免异步回调的时序问题。

如果需要实时监听数据更新(比如UI需要同步数据库变化),应在Widget层使用FirebaseList或StreamBuilder,而非在异步数据方法中依赖其回调收集数据。


内容的提问来源于stack exchange,提问作者David L

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:35:29