如何从递归方法返回Promise?Firebase嵌套数据获取遇无限循环
解决Firebase嵌套集合递归查询的无限循环问题
你的递归逻辑里存在几个关键问题,导致了无限循环和结果丢失,我来一步步拆解并修复:
原代码的核心问题
- 共享对象引用导致参数污染:你在递归中直接修改同一个
params对象,比如在forEach里循环赋值params.formationId,所有递归调用都会共享这个引用,最终所有请求都会指向最后一个formationId,后续修改还会覆盖之前的结果。 - Promise处理逻辑混乱:在
forEach里直接调用resolve(callMe(params))并把结果push到promises数组,这完全不符合Promise的工作方式——resolve会立即结束当前Promise,而且这里push的不是Promise实例,导致Promise.all根本起不到并行处理的作用。 - 属性判断语法错误:
!params.formations[params.formationId].hasOwnProperty.modules是错误写法,正确的应该是!params.formations[params.formationId].hasOwnProperty('modules'),你少了属性名的括号。 - 缺失递归终止条件:当所有Formation的Modules都加载完成后,没有逻辑终止递归,导致代码一直循环调用。
修复后的测试代码
我重构了你的代码,解决了上述问题,同时用async/await让逻辑更清晰易懂:
function fakeWait(toReturn) { return new Promise((resolve) => { setTimeout(() => { resolve(toReturn); }, 1000); // 缩短等待时间方便测试 }); } async function callMe(params = null) { // 初始调用:获取所有Formations if (params === null) { const formations = await fakeWait({ id1: { title: 'Title1' }, id2: { title: 'Title2' }, id3: { title: 'Title3' }, id4: { title: 'Title4' }, }); // 并行处理每个Formation,避免引用污染 await Promise.all( Object.keys(formations).map(async (formationId) => { const formation = formations[formationId]; // 拉取当前Formation的Modules formation.modules = await fakeWait({ id1: { title: 'Title1.1' }, id2: { title: 'Title1.2' }, id3: { title: 'Title1.3' }, id4: { title: 'Title1.4' }, }); // 如果需要继续递归拉取Chapters/Screens,在这里扩展逻辑即可 // 示例:await fetchChapters(formation.modules); }) ); return formations; } } // 调用测试 callMe() .then((results) => console.log('最终结果:', results)) .catch((msg) => console.log('错误:', msg));
针对Firebase实际场景的优化建议
结合你给出的Firebase数据结构(Formations → Formation → Modules → Module → Chapters → Chapter → Screens → Screen),可以封装一个通用的嵌套集合拉取函数,灵活处理任意深度的嵌套数据:
// 通用函数:拉取文档的嵌套子集合 async function fetchNestedCollections(docRef, nestedPaths) { const docSnap = await docRef.get(); if (!docSnap.exists()) return null; const data = { id: docSnap.id, ...docSnap.data() }; // 如果还有嵌套路径需要拉取 if (nestedPaths.length > 0) { const [currentPath, ...remainingPaths] = nestedPaths; const collectionRef = docRef.collection(currentPath); const docsSnap = await collectionRef.get(); // 并行拉取每个子文档的嵌套集合 data[currentPath] = await Promise.all( docsSnap.docs.map((doc) => fetchNestedCollections(doc.ref, remainingPaths)) ); } return data; } // 使用示例:拉取所有Formations及其嵌套的Modules/Chapters/Screens async function fetchAllFormations() { const formationsRef = firebase.firestore().collection('Formations'); const formationsSnap = await formationsRef.get(); return Promise.all( formationsSnap.docs.map((doc) => fetchNestedCollections(doc.ref, ['Modules', 'Chapters', 'Screens']) ) ); }
这个通用函数用Promise.all并行处理请求提升性能,同时避免了引用共享的问题,能轻松适配你需要的多层嵌套数据拉取场景。
内容的提问来源于stack exchange,提问作者Jonathan Lafleur
相关产品推荐
相关产品推荐

