【问题标题】:Firebase not receiving data before view loaded - empty array returned before filledFirebase 在加载视图之前未接收到数据 - 在填充之前返回空数组
【发布时间】:2017-10-08 06:16:16
【问题描述】:

在下面的代码中,我将每个项目的键和电子邮件地址保存在一个表中,并使用所述键检索要从原始表中获取的对象。我可以看到,当我使用 console.log 时,这些项目正在被放入 rawList 数组中,但是该函数在其中包含任何内容之前返回了 this.cartList,因此视图不会接收任何数据。我怎样才能让this.cartList 等待rawList 已满再返​​回?

 ionViewWillEnter() {
    var user = firebase.auth().currentUser;
    this.cartData.getCart().on('value', snapshot => {
        let rawList = [];
        snapshot.forEach(snap => {
          if (user.email == snap.val().email) {
            var desiredItem = this.goodsData.findGoodById(snap.val().key);
            desiredItem.once("value")
            .then(function(snapshot2) {
              rawList.push(snapshot2);
            });
            return false
          }
        });
        console.log(rawList);
        this.cartList = rawList;
    });
  }

我尝试将 this.cartList = rawList 放在多个不同的位置(在 return false 之前,甚至在 .then 语句内,但这并没有解决问题。

【问题讨论】:

  • return 语句仅适用于同步工作。 Firebase 查询是异步的,因此您需要通过回调返回数组。

标签: javascript angular firebase callback firebase-realtime-database


【解决方案1】:

以下函数调用是异步的,您在rawList 有机会更新之前超出范围,因为此数据库调用需要相当长的时间:

desiredItem.once("value").then(function(snapshot2) {
    rawList.push(snapshot2);
});

当您应该推送 snapshot2.val() 以获取原始值时,您还将快照直接推送到此列表。

以下是我将如何修复您的代码:

ionViewWillEnter() {
    var user = firebase.auth().currentUser;
    this.cartData.getCart().on('value', snapshot => {
        // clear the existing `this.cartList`
        this.cartList = [];
        snapshot.forEach(snap => {
          if (user.email == snap.val().email) { 
            var desiredItem = this.goodsData.findGoodById(snap.val().key);
            desiredItem.once("value")
            .then(function(snapshot2) {
              // push directly to the cartList
              this.cartList.push(snapshot2.val());
            });
          }
          return false;
        });
    });
  } 

【讨论】:

  • 我收到了Unsyntactic continue ... You may need an appropriate loader to handle this file type
  • 我收到与stackoverflow.com/questions/39845758/… 相同的错误消息,这可能是错误存在的原因
  • @maudulus 抱歉,我不知道这一点。试试我更新的答案。
  • Uncaught (in promise): TypeError: undefined is not an object (evaluating 'this.cartList') - 某种范围界定问题
【解决方案2】:

问题在于 forEach 循环(同步)内的 Promise(异步 .once() 调用 firebase)。 forEach 循环不会等待 then() 语句,因此在下一次迭代中,前一次迭代的数据就会丢失......

let snapshots = [1, 2, 3];
let rawList = [];

snapshots.forEach((snap) => {
  console.log(rawList.length)
  fbCall = new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve("Success!");
    }, 2500)
  });
  fbCall.then((result) => {
    rawList.push(result);
  });
})

您需要 forEach 将整个 Promise 推送到 rawList,然后等待它们解决并处理结果。

var snapshots = [1, 2, 3];
var rawList = [];
var counter = 0;

snapshots.forEach((snap) => {
  console.log(rawList.length)
  var fbCall = new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve("Success!" + counter++);
    }, 1500)
  });
  rawList.push(fbCall);
})

Promise.all(rawList).then((res) => {
  console.log(res[0]);
  console.log(res[1]);
  console.log(res[2]);
});

问题是,分配 this.cartList = Promise.all(rawList) 仍然有点尴尬,因为它使它成为 Promise。所以您可能想重新考虑您的设计并制作类似于 getCartList 服务的东西? (不知道你的应用是什么样的:p)

【讨论】:

    【解决方案3】:

    由于您使用的是 Angular,您还应该使用 angularfire2,它使用了 Observables,它将为您解决这个问题。您仍然会在很多事情上使用普通的 SDK,但对于获取和绑定数据,不建议在没有 angularfire2 的情况下单独使用 Firebase,因为这会使这些事情变得更难管理。

    这种方法的好处是您可以利用Observable 上的任何方法,例如filterfirstmap 等。

    安装后只需执行以下操作:

    public items$: FirebaseListObservable<any[]>;
    
    this.items$ = this.af.database.list('path/to/data');
    

    在视图中:

    {{items$ | async}}
    

    为了等待数据出现。

    【讨论】:

      【解决方案4】:

      使用AngularFire2RxJS 这将节省您大量的时间,并且您将通过使用RxJS 运算符以正确且可维护的方式进行操作,您可以在此处了解这些运算符learnrxjs

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 2019-12-16
        • 1970-01-01
        • 1970-01-01
        • 2017-05-06
        • 1970-01-01
        相关资源
        最近更新 更多