【问题标题】:How to return promise from recursive method如何从递归方法返回承诺
【发布时间】:2018-11-07 09:42:45
【问题描述】:

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

由于我需要递归触发数据库,​​因此我正在尝试创建一个自调用递归方法,该方法将映射实际的数据库架构并提取所需的数据。

出于本文的目的,我创建了假方法来展示我的实际逻辑。也许有人可以帮助我,这是一个永无止境的循环,无限触发最后一项。它从不存储第一个结果

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

您还可以在此处查看和尝试堆栈闪电战中的代码:https://stackblitz.com/edit/js-wuvp9z

Firebase 数据结构:

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

【问题讨论】:

标签: javascript firebase promise


【解决方案1】:

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

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

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

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

3/ 为每个文档(Formation、Module 和 Chapter)添加一个名为 nbrScreens 的编号类型字段,值为 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>

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

你可以像(伪代码):

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 的promise,如here 所述。因此,您必须使用then() 链接它们。

2/ 另一方面,transaction.set() 返回“This Transaction instance”,参见here。因此,您应该一个接一个地链接它们(例如return transaction.set().set()...)

【讨论】:

  • 非常感谢您的时间和解释!我真的很感激!
猜你喜欢
  • 2018-10-11
  • 2019-05-25
  • 2018-01-28
  • 1970-01-01
  • 2020-01-24
  • 2017-04-02
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多