【问题标题】:Firebase (Angular) Promises with Loops in LoopsFirebase (Angular) Promises with Loops in Loops
【发布时间】:2017-01-02 06:41:37
【问题描述】:

只有在所有的 promise 都解决之后,你必须在循环中等待循环中的异步调用,你如何执行一个函数?

代码被简化到最低限度

$scope.book = function(input) {

  //Get the cart items from DB
  ref.child('/cart/' + userId).once('value').then(function(cartSnap) {

    //Loop through the cart items
    cartSnap.forEach(function(cartItemSnap) {

      var itemId = cartItemSnap.key();

      //Get the inventory items from DB that are in the cart
      ref.child('/inventory/' + itemId).once('value').then(function(inventoryItem) {

        //Loop through booking IDs of inventoryItem
        inventoryItem.child('rentals').forEach(function(rentalSnap) {

          var rentalId = rentalSnap.key();

          //Get bookings from rental/active
          ref.child('/rental/'+ rentalId).once('value').then(function(activeRentals) {

            checkIfBookingIsAllowed();

          });
        });
      });
    });

    //Once everything was checked
    bookRental();
  });
};

为了提高速度,所有请求都可以并行发出,但最终函数 bookRental() 只能在一切都解决后调用。

感谢您的帮助。

编辑: 又一次失败的尝试。 Promise.all('collector') 在所有承诺解决之前被触发。所以“完成”出现在控制台中的所有“检查”之前。

$scope.book = function(input) {

  //Get the cart items from DB
  ref.child('/cart/' + userId).once('value').then(function(cartSnap) {

    //Promise collector
    var collector = [];

    //Loop through the cart items
    cartSnap.forEach(function(cartItemSnap) {

      var itemId = cartItemSnap.key();

      //Get the inventory items from DB that are in the cart
      var promise1 = ref.child('/inventory/' + itemId).once('value').then(function(inventoryItem) {

        //Loop through booking IDs of inventoryItem
        inventoryItem.child('rentals').forEach(function(rentalSnap) {

          var rentalId = rentalSnap.key();

          //Get bookings from rental/active
          var promise2 = ref.child('/rental/'+ rentalId).once('value').then(function(activeRentals) {

            console.log('check');
            collector.push(promise2);

          });
        });

        collector.push(promise1);
      });
    });

    //Once everything was checked
    Promise.all(collector).then(function() {
      console.log('Done');
    });
  });
};

【问题讨论】:

  • checkIfBookingIsAllowed 是做什么的?能否请您添加此代码?
  • 它检查日期重叠和项目的数量。这与我的问题并不相关。您可以简单地将这一行与 console.log('check');
  • @FrankvanPuffelen 感谢您的帮助。我试过了,检查我的新代码。我错过了什么?
  • 您正在异步加载数据,因此需要确保您的承诺“冒泡”。在没有测试的情况下写答案有点棘手。您是否有机会在 jsbin 中重现该问题,以便我可以解决问题?

标签: javascript firebase promise firebase-realtime-database


【解决方案1】:

你收集承诺为时已晚。 现在收集它们,而不是以后。 .thens 中的代码稍后运行。

下面是立即运行的:

  1. 两个嵌套的 for 循环都运行完成,减去 .thens 中的所有代码。
  2. Promise.all(collector)

此时collector 仍然是空的,所以Promise.all() 完成得非常快。

collector.push(promise) 调用移到.thens 之外。

更实用的方法:

通过上述修复,您的代码应该可以工作,但更简洁的方法是使用return all promises。虽然forEach 不允许返回值,但map 允许,因此可以将其重写为(简化):

Promise.all(cartSnap.map(snap => ref.child(itemUrl).once('value').then(item =>
  Promise.all(item.child('rentals').map(snap => ref.child(url).once('value'))))))
.then(allActiveRentals => console.log('Done'));

【讨论】:

  • 感谢 jib 采用这种方法。 firebase 不返回一个数组,而是一个不包含 map 函数的对象。还有其他方法吗?
  • @stefan 不管你在做什么forEach 是一个数组,所以你可以使用map 代替。在这样的对象上使用mapObject.keys(obj).map(key => foo(obj[key]))
  • 好的,这适用于对象。现在我只是在逻辑上挣扎。我做了一个codepen,但第一个循环仍然在嵌套循环之前完成。我知道为什么,但不知道如何正确地做到这一点。
  • 你仍然不是returning all the promises,具体来说,你缺少return Promise.all(请注意,对于箭头函数,不使用括号时返回是隐式的,即x => 2*x具有隐式返回,比如x => { return 2*x; }。同样仍然不确定cartSnap 是什么类型,你不再调用.key(),那么itemId 是否正确?
  • 好的,在我返回所有的承诺之后,它似乎工作了!谢谢。是的 itemId 已经是关键。我没有尝试使用箭头功能。但这里是更新版本:codepen
猜你喜欢
  • 2022-11-05
  • 2022-12-02
  • 1970-01-01
  • 2021-01-25
  • 2011-04-22
  • 1970-01-01
  • 2016-09-19
  • 2016-03-08
相关资源
最近更新 更多