Firebase (Angular) Promises with Loops in Loops



你如何执行一个函数后,所有的承诺都解决了,你必须等待在循环内循环的异步调用?

代码被简化到最小

$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')在所有承诺被解析之前被触发。所以'done'会在控制台的所有' checking '之前出现。

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

你收回承诺太晚了。现在就收集,而不是以后。.then s中的代码稍后运行。

下面是立即运行的代码:

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

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

collector.push(promise)调用移出.then调用。

更实用的方法:

有了上面的修复,你的代码应该可以工作了,但是更简洁的方法是返回所有的承诺。虽然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'));

最新更新