2017-02-05 11 views
0

Я пытаюсь пропустить список Firebase (используя AngularFire2), и я хочу получить Promise, чтобы указать, когда завершились вложенные циклы, чтобы я мог обработать второе действие. Я использую следующий код:Получение обещания из вложенного цикла forEach?

console.log('START'); 
    this.af.database.list('Sites/123123/Users') 
    .map(users => { 
     users.map(user => { 
     console.log('found a user ', user); 
     this.af.database.list('registeredUsers/' + user.$key + '/Sites') 
      .forEach(sites => { 
      sites.forEach(site => { 

       if (site.$value == '123123') { 
       console.log('found site ', site.$key); 
       } 
      }); 
      }); 
     }); 
    }).first().toPromise().then(function (x) { 
     console.log('ALL DONE') 
    }); 

Выход:

console output

Но я получаю обещание только после внешнего цикла, не дожидаясь вложенного цикла, чтобы закончить. Какие-либо предложения?

+0

Если вы создаете несколько обещаний с вашей '.map()' петли, то вам нужно будет использовать 'Promise.all()' ждать их. 'Promise.all()' создает единственное обещание (которое вы можете вернуть), которое ждет массив обещаний. – jfriend00

+0

где поставить Promise.all()!? – devMan

+0

Я не знаю вашего интерфейса с базой данных, но предположительно 'this.af.database.list()' является асинхронным, и вам нужно получить от него обещание и вернуть его из '.map()'. Это означает, что '.map()' будет генерировать массив обещаний, которые вам нужно использовать 'Promise.all()' в массиве обещаний, чтобы вы могли вернуть единственное обещание, которое создает 'Promise.all()'. – jfriend00

ответ

1

Вы отсутствующие операторы возврата в карте вызовов, потому что вы создаете новые Наблюдаемые внутри первой карты на пользователях наблюдаемого вы должны использовать mergeMap здесь:

console.log('START'); 
this.af.database 
    .list('Sites/123123/Users') 
    .mergeMap(users => { 
     return users.map(user => { 
      console.log('found a user ', user); 
      return this.af.database.list('registeredUsers/' + user.$key + '/Sites') 
       .forEach(sites => { 
        sites.forEach(site => { 
         if (site.$value == '123123') { 
          console.log('found site ', site.$key); 
         } 
        }); 
       }); 
     }); 
    }) 
    .first().toPromise().then(x => { 
     console.log('ALL DONE') 
    }); 

Здесь у меня есть упрощенный вариант ваши кода, устраняя зависимость для вашего firebase приложения:

//const Rx = require('rxjs'); // require Rx on nodejs instead of html script tag 
 
const usersObservable = Rx.Observable.from([[{ id: 1, $key: 123 }]]); 
 

 
const sitesObservable = Rx.Observable.from(
 
    new Promise((resolve) => { 
 
     setTimeout(() => { 
 
      resolve([{ $value: '123123', $key: 'site1' }, { $value: '0', $key: 'site2' }]) 
 
     }, 500); 
 
    }) 
 
); 
 

 
console.log('START'); 
 
usersObservable 
 
    .mergeMap((users) => { 
 
     return users.map(user => { 
 
      console.log('found a user ', user); 
 
      return sitesObservable 
 
       .forEach(sites => { 
 
        sites.forEach(site => { 
 
         if (site.$value == '123123') { 
 
          console.log('found site ', site.$key); 
 
         } 
 
        }); 
 
       }); 
 
     }); 
 
    }) 
 
    .first().toPromise().then(x => { 
 
     console.log('ALL DONE') 
 
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js"></script>

Вот генерируемый вывод:

START 
found a user { 
    "id": 1, 
    "$key": 123 
} 
found site site1 
ALL DONE 

С вашей конкретной firebase кода добавил ваш код должен выглядеть следующим образом:

console.log('START'); 
var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users'); 

siteUsers 
    .mergeMap(users => { 
     console.log('users = ', users); 
     return users.map(user => { 
      console.log('found a user ', user); 
      return this.af.database.list('registeredUsers/' + user.$key + '/Sites') 
       .forEach(sites => { 
        console.log('sites = ', sites) 
        sites.forEach(site => { 
         if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') { 
          console.log('found site ', site.$key); 
         } 
        }); 
       }); 
     }); 
    }) 
    .first().toPromise().then(x => { 
     console.log('ALL DONE') 
    }); 
+0

Я тоже пробовал это, так же, его продолжаю получать обещание после внешнего цикла, не дожидаясь завершения вложенных – devMan

+0

, если вы не можете получить правильное поведение с операторами return в функциях карты, убедитесь, что все '* .database.list (...) 'вызывает возврат наблюдаемого и простого массива – Kalle

+0

database.list (..) return FirebaseListObservable devMan

1

@Kalle

я попробовал этот способ тоже, и то же самое:

console.log('START'); 
    var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users'); 
    const siteUsersObservable = Observable.from(siteUsers); 

    siteUsersObservable 
    .map(users => { 
     console.log('users = ', users); 
     return users.map(user => { 
     console.log('found a user ', user); 
     var userSites = this.af.database.list('registeredUsers/' + user.$key + '/Sites'); 
     const userSitesObservable = Observable.from(userSites); 
     return userSitesObservable 
      .forEach(sites => { 
      console.log('sites = ', sites) 
      sites.forEach(site => { 
       if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') { 
       console.log('found site ', site.$key); 
       } 
      }); 
      }); 
     }); 
    }) 
    .first().toPromise().then(x => { 
     console.log('ALL DONE') 
    }); 

и структура базы огня такова:

и мой результат: