如何从递归方法返回promise

时间:2018-05-28 19:20:44

标签: javascript firebase promise

我实际上是在一个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上的代码:https://stackblitz.com/edit/js-wuvp9z

Firebase数据结构:

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

1 个答案:

答案 0 :(得分:1)

以下是Firestore事务的代码,该事务将编写新的Screen文档并分别更新Formation,Module和Chapter父文档中的nbrScreens个计数器。

这是一个完整的HTML页面。请执行以下操作:

1 /在HTML中调整您的Firebase配置值。

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

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

4 /在HTML页面中调整screenIdscreenData的值以及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>

请注意,您可以调整代码以创建编队,模块和章节。事实上,这不能在事务中完成,因为所有读取都应在写入之前执行

你可能会喜欢(伪代码):

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);
});

最后,以下是关于上述交易代码的几个值得注意的要点:

1 / transaction.get()返回包含非空firebase.firestore.DocumentSnapshot的承诺,如here所述。因此,您必须使用then()链接它们。

2 /另一方面,transaction.set()返回“此事务实例”,请参阅here。因此,您应该一个接一个地链接它们(如return transaction.set().set()...