Javascript 如何从递归方法返回承诺

Javascript 如何从递归方法返回承诺,javascript,firebase,promise,Javascript,Firebase,Promise,实际上,我正在一个Javascript项目中使用Firebase,并试图获取集合中所有元素的嵌套子元素的信息 因为我需要递归地触发db,所以我尝试创建一个自调用递归方法,该方法将映射实际的db模式并提取所需的数据 为了写这篇文章,我创建了一个假方法来演示我的实际逻辑。也许有人能帮我,这是一个永无止境的循环,无限地触发最后一项。它从不存储第一个结果 function fakeWait(toReturn) { return new Promise((resolve) => { se

实际上,我正在一个Javascript项目中使用Firebase,并试图获取集合中所有元素的嵌套子元素的信息

因为我需要递归地触发db,所以我尝试创建一个自调用递归方法,该方法将映射实际的db模式并提取所需的数据

为了写这篇文章,我创建了一个假方法来演示我的实际逻辑。也许有人能帮我,这是一个永无止境的循环,无限地触发最后一项。它从不存储第一个结果

function fakeWait(toReturn) {
  return new Promise((resolve) => {
    setTimeout(function(){ resolve(toReturn); }, 8000);
  });
}

function callMe(params = null) {
  return new Promise((resolve, reject) => {
   console.log('Promise called', {...params});

    const promises = [];

    if (params === null) {
      promises.push(fakeWait(
        {
          id1:{title:'Title1'},
          id2:{title:'Title2'},
          id3:{title:'Title3'},
          id4:{title:'Title4'},
        }).then(results => {
          params = {};
          params.formations = results;
          resolve(callMe(params));
        }));
    }

    else {
      if (!params.hasOwnProperty('formationId') && 
          params.hasOwnProperty('formations')) {
        Object.keys(params.formations).forEach(formationId => {
          params.formationId = formationId;
          promises.push(resolve(callMe(params)));
        });
            }

      else if (params.hasOwnProperty('formationId') && 
               params.hasOwnProperty('formations') && 
               !params.formations[params.formationId].hasOwnProperty.modules) 
      {
        promises.push(fakeWait({
          id1:{title:'Title1.1'},
          id2:{title:'Title1.2'},
          id3:{title:'Title1.3'},
          id4:{title:'Title1.4'},
        }).then(result => {
          params.formations[params.formationId].modules = result;
          resolve(callMe(params));
        }))
            }
    }

    Promise.all(promises).then(() => { console.log('Resolved.'); resolve(params); }).catch(()=> reject('oops'));
  });
}

callMe().then(results =>console.log(results)).catch(msg => console.log(msg));
您也可以在此处查看和尝试stackblitz上的代码:

Firebase数据结构:

Formations - Collection
- Formation - Document
-- Modules - Collection
--- Module - Document
---- Chapters - Collection
----- Chapter - Document
------ Screens - Collection
------- Screen - Document

以下是Firestore事务的代码,该事务将写入新的屏幕文档,并分别在表单、模块和章节父文档中更新屏幕计数器

这是一个完整的HTML页面。按以下步骤进行:

1/在HTML中调整Firebase配置值

2/在Firestore中创建具有所需ID的父集合和文档

3/对于每个文档(表格、模块和章节),添加一个名为
nbr
的类型编号字段,其值为0

4/在HTML页面中,将
screenId
screenData
的值和
setNewScreen()
函数调用的参数调整为所需的值

5/在浏览器中打开页面(例如本地),将调用函数并执行事务

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://www.gstatic.com/firebasejs/5.0.4/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.0.4/firebase-firestore.js"></script>
</head>

<body>

<script>

    var config = {
        apiKey: "...",
        authDomain: "...",
        databaseURL: "...",
        projectId: "..."
    };

    firebase.initializeApp(config);

    var firestoredb = firebase.firestore();


    function setNewScreen(formationId, moduleId, chapterId, screenId, screenData) {

        var formationDocRef = firestoredb.collection("Formations").doc(formationId);
        var moduleDocRef = formationDocRef.collection("Modules").doc(moduleId);
        var chapterDocRef = moduleDocRef.collection("Chapters").doc(chapterId);
        var screenDocRef = chapterDocRef.collection("Screens").doc(screenId);

        return firestoredb.runTransaction(function (transaction) {

            var newChaptersNbrScreens;
            var newModulesNbrScreens;
            var newFormationsNbrScreens;

            return transaction.get(chapterDocRef)
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Chapter does not exist!";
                    }

                    newChaptersNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction.get(moduleDocRef);

                })
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Module does not exist!";
                    }

                    newModulesNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction.get(formationDocRef);

                })
                .then(function (sfDoc) {

                    if (!sfDoc.exists) {
                        throw "Document Formation does not exist!";
                    }

                    newFormationsNbrScreens = sfDoc.data().nbrScreens + 1;
                    return transaction
                        .set(formationDocRef, {nbrScreens: newFormationsNbrScreens}, { merge: true })
                        .set(moduleDocRef, {nbrScreens: newModulesNbrScreens}, { merge: true })
                        .set(chapterDocRef, {nbrScreens: newChaptersNbrScreens}, { merge: true })
                        .set(screenDocRef, screenData)

                });

        });

    }


    //Calling the function
    //The formation, module and chapter docs must be created before calling it!!

    var screenId = 's1';
    var screenData = {title: 'screenTitle_s1', content: 'foo'};

    setNewScreen("f1", "m1", "c1", screenId, screenData)  //Screen will be saved under Formation f1, Module m1 and Chapter c1
        .then(function () {
            console.log("Transaction successfully committed!");
        })
        .catch(function (error) {
            console.log("Transaction failed: ", error);
        });


</script>


</body>
</html>

变量配置={
apiKey:“…”,
authDomain:“…”,
数据库URL:“…”,
投射物:“…”
};
firebase.initializeApp(配置);
var firestoredb=firebase.firestore();
函数setNewScreen(formationId、moduleId、chapterId、screenId、screenData){
var formationDocRef=firestoredb.collection(“Formations”).doc(formationId);
var moduleDocRef=formationDocRef.collection(“Modules”).doc(moduleId);
var chapterDocRef=模块化信用集合(“章节”).doc(章节ID);
var screenDocRef=chapterDocRef.collection(“Screens”).doc(screenId);
返回firestoredb.runTransaction(函数(事务){
var Newchaptersnbr屏幕;
var NewModulesNBR屏幕;
var新格式;
返回事务.get(chapterDocRef)
.then(功能(sfDoc){
如果(!sfDoc.存在){
抛出“文档章节不存在!”;
}
newchaptersnbrscreenses=sfDoc.data().nbrscreenses+1;
返回事务.get(moduleDocRef);
})
.then(功能(sfDoc){
如果(!sfDoc.存在){
抛出“文档模块不存在!”;
}
newModulesNbrScreens=sfDoc.data().nbrScreens+1;
返回事务.get(formationDocRef);
})
.then(功能(sfDoc){
如果(!sfDoc.存在){
抛出“文档格式不存在!”;
}
NewFormationsNbScreens=sfDoc.data().NbScreens+1;
退货交易
.set(formationDocRef,{nbrscreenses:newformationsNBrscreenses},{merge:true})
.set(moduleDocRef,{NBRScreenses:newModulesNBRScreenses},{merge:true})
.set(chapterDocRef,{nbrscreenses:newchaptersnbrscreenses},{merge:true})
.set(screenDocRef、screenData)
});
});
}
//调用函数
//在调用之前,必须创建表单、模块和章节文档!!
变量screenId='s1';
var screenData={title:'screenttitle_s1',content:'foo'};
setNewScreen(“f1”、“m1”、“c1”、screenId、screenData)//屏幕将保存在表格f1、模块m1和章节c1下
.然后(函数(){
log(“事务已成功提交!”);
})
.catch(函数(错误){
日志(“事务失败:”,错误);
});
请注意,您可以修改代码以创建编队、模块和章节。事实上,这不能在事务中完成,因为所有的读取都必须在写入之前执行

您可以使用(伪代码):


FormationDocumentReference.set({})//注释不用于扩展讨论;这段对话已经结束。非常感谢您的时间和解释!我真的很感激!
FormationDocumentReference.set({}) // <- returns a promise
.then(function() {
    ModuleDocumentReference.set({}) // <- returns a promise
})
.then(function() {
    ChapterDocumentReference.set({}) // <- returns a promise
})
.then(function() {
    setNewScreen(id_f, id_m1, id_c1, screenId, screenData)  // <- returns a promise
.then(function () {
    console.log("Transaction successfully committed!");
})
.catch(function (error) {
    console.log("Transaction failed: ", error);
});