【问题标题】:Cannot "capture" Firebase snapshot in an array using a promise无法使用 Promise 在数组中“捕获”Firebase 快照
【发布时间】:2018-01-29 20:47:06
【问题描述】:

我知道有很多关于使用 Javascript 承诺加载返回的 Firebase 对象的问题和答案,但我还没有成功捕获数组中的数据。

问题:我已经设置了一个数组(下面的productArray)以迭代方式获取 Firebase 快照的值,但在退出 forEach 循环后该数组仍然为空。

database.js:

  getProductList = function(){

    let productArray =[];

    loadFBData = function(){
      firebase.initializeApp(config);
      return firebase.database().ref().once("value", function(snapshot){
        return snapshot.val();
    });
  }

  Promise.all([loadFBData()]).then(function(snapshot) {
    snapshot.forEach(function(product) {
      productArray.push(product.val());
    });
  });
}

问题:我认为我使用 Promise.all 错误,但不知道如何(也是新的异步函数)。感谢您提供有关如何让productArray 成功加载的任何提示。

编辑: 详细地说,snapshot 似乎正在加载正确的 Firebase 数据,forEach 正确循环通过 snapshot 中的每个项目,但 productArray 变为(或仍然)在Promise.all 语句之外为空。

更新我实现了以下所有建议,但程序不会停止足够长的时间来从快照对象构建数组。去年我使用了 Angularfire 的 $loaded 函数,它在让程序等待方面非常有效,所以我不明白为什么普通的 JS promise 在这里不起作用。求助于timeout 有意义吗?我什至尝试结合 alexmac 和 Pointy 的建议,但没有雪茄:

 getProductList = function(){

    let productArray =[];

    loadFBData = function(){
      firebase.initializeApp(config);

      return new Promise(resolve => {
         firebase.database().ref().once('value')
            .then(function(snapshot){
               return resolve(function(snapshot){
                  productArray = Object.keys(snapshot).map((key, prod) => prod);
               });
            });
         });
   }

  loadFBData();
}

【问题讨论】:

  • 乍一看,这段代码看起来不错。它只会使用一个承诺,因为您正在加载整个数据库。但这应该可行,除非我忽略了某些东西。这段代码有什么问题?
  • @Frank,感谢您的回复。详细地说,snapshot 确实在加载 Firebase 数据。问题在于(或必须)在 forEach 循环中,因为(在 Promise.all 之后和 getProductList() 内部),productArray.length 返回 0。
  • 您的代码实际上根本没有做出承诺。 loadFBData() 函数需要使用 Promise 回调中的当前代码返回一个新的 Promise。该函数应将检索到的数据传递给resolve() 函数。
  • 为什么你只对一个元素使用Promise.all?此外,如果 firebase.database().ref().once 返回一个承诺,则您根本不需要 Promise.all

标签: javascript firebase firebase-realtime-database promise


【解决方案1】:

背景

我认为您缺少对 Promise 和 Firebase SDK 的理解。

首先,请注意所有 Firebase SDK 函数都会返回 Promise,因此您无需创建自己的 Promise。其次,Promise.all 用于组合多个 Promise。一个简单的 Promise 可以用一个简单的 .then() 链接起来。

我这里有一个关于保存和查询数据的视频:Save and Query Firebase Data

我在这里有另一个关于承诺的视频:Promises

加载 Firebase

您应该在页面顶部初始化 firebase。只需要初始化一次,所以不要在函数中初始化。

您可以在页面上加载 Firebase SDK 后立即调用firebase.initializeApp(config)。或者您可以参考 Firebase Hosting 的init.js,如下所示。

示例

以下示例加载 Firebase SDK,使用我的测试数据库的 init.js 文件初始化 firebase,然后查询、操作并返回一个数组。

请注意,Firebase 不支持零索引数组,因此您从 Firebase 返回的所有内容都是未排序的对象。这个例子展示了几种不同的返回数据的方式:)

<html>
  <head>
    <script src="https://www.gstatic.com/firebasejs/4.3.0/firebase.js"></script>
    <script src="https://quiver-two.firebaseapp.com/__/firebase/init.js"></script>    
    <script>
      function loadFirebaseArray(path) {
        return firebase.database().ref(path).once('value').then(snap => snap.val());
      }

      function toArray(items) {
        return Object.keys(items).reduce((array, key) => {
          const value = items[key];
          array.push({key, value});
          return array;
        }, []).sort();
      }

      loadFirebaseArray('stackoverflow').then(items => {
        console.log('items', items);

        const asArray = toArray(items);
        alert(JSON.stringify(asArray));

        const justTheValues = asArray.map(x => x.value).sort();
        alert(justTheValues);
      });
    </script>
  </head>

</html>

【讨论】:

    【解决方案2】:

    一个工作示例可能如下所示:

    getProductList = () => {
      let loadFBData = () => {
        firebase.initializeApp(config);
        return new Promise(resolve => {
          firebase.database().ref().once('value', resolve);
        });
      }
    
      let productArray =[];
      loadFBData()
        .then(snapshot => {
          productArray = Object.keys(snapshot).map((key, prod) => prod);
    }
    

    在这里,我使用 promisificationfirebase 查询创建一个promise。所以loadFBData 返回了一个promise,我可以创建一个promise 链或者使用ES7 async/await 等到promise 将被实现再使用result。

    【讨论】:

    • 感谢我目前正在尝试 ES6 解决方案,并正在尝试解决为什么我收到 Uncaught (in promise) TypeError: snapshot.map is not a function
    • productArray = snapshot 之前添加断点或console.log(snapshot) 并检查snapshot 是什么。根据您的代码,它必须是一个数组。
    • snapshot 填充了一个看起来是典型 Firebase 格式的 JSON 对象:{"key": {...}, "key": {...}, ... }。其中key 是(在我的情况下)一个表示整数的字符串。我想知道对象是否以某种方式太深,但实际上只有 2 个级别:key 和 {obj}。无论如何,快照是一个对象而不是数组。
    • 我正在尝试遍历 snapshot 对象中的每个项目并将它们添加/推送到数组中。但如果你说检索到的snapshot 应该是一个数组开头,那么我可能在这里处理的是完全不同的蠕虫罐头。
    • 更新了答案,遍历snapshot 属性。
    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 2018-11-30
    • 2019-03-03
    • 2018-10-16
    • 2017-05-16
    • 2017-07-27
    • 1970-01-01
    • 2017-06-04
    相关资源
    最近更新 更多