如何无需setTimeout等待Firebase实时数据库循环查询结果
问题描述
尝试从成员列表获取每个成员的详细数据,但总是返回undefined,目前用setTimeout等待数据加载,希望去掉setTimeout,让结果等所有循环查询完成后再返回,现有代码如下:
database().ref(`/group/${groupid}`) .on("value", snapshot => { let data_1 = snapshot.val(); let members = ''; let i = 0; for(let memberid in data_1) { i++; database().ref(`/members/${memberid}`) .once("value", snapshot => { let data_2 = snapshot.val(); members += '"'+memberid+'":{"uid":"'+data_2.uid+'","name":"'+data_2.name+'","avatar":"'+data_2.avatar+'"},'; }) } let duration = parseInt(i)*125; setTimeout(() => { let resmembers = '{'+members.slice(0, -1)+'}'; let membersobj = JSON.parse(resmembers); return membersobj; },parseInt(duration)) })
解决方案
核心问题是循环里的once是异步操作,不会阻塞循环,字符串拼接的方式赶不上异步请求的速度,只能靠猜延迟的setTimeout凑数。改用Promise和Promise.all统一等待所有异步请求完成即可:
- 把每个成员的查询包装成Promise
- 收集所有Promise到数组,用
Promise.all等待全部完成 - 处理返回结果,直接构建最终的成员对象
修改后的代码如下:
database().ref(`/group/${groupid}`) .on("value", async snapshot => { const data_1 = snapshot.val(); // 收集所有成员查询的Promise const memberPromises = []; for (const memberid in data_1) { const promise = new Promise((resolve) => { database().ref(`/members/${memberid}`) .once("value", memberSnapshot => { const data_2 = memberSnapshot.val(); // 直接返回结构化的成员数据,避免手动拼接JSON字符串 resolve({ [memberid]: { uid: data_2.uid, name: data_2.name, avatar: data_2.avatar }}); }); }); memberPromises.push(promise); } // 等待所有查询完成 const memberResults = await Promise.all(memberPromises); // 合并所有结果为一个对象 const membersobj = Object.assign({}, ...memberResults); // 此处可直接使用membersobj,无需setTimeout console.log(membersobj); // 若需外部使用该数据,可调用自定义处理函数 // handleMembersData(membersobj); })
补充说明:
- 用
async/await简化异步代码逻辑,可读性更强 - 直接构建对象而非拼接JSON字符串,减少语法错误概率
Promise.all会并行等待所有请求完成,效率高于逐个等待- 注意
on回调本身是异步的,内部return无法将结果返回至外部,需通过调用处理函数传递数据
内容的提问来源于stack exchange,提问作者riski
相关产品推荐
相关产品推荐

