Как ждать неопределенных рекурсивных обещаний, чтобы решить

У меня проблемы с контролем потока в моем приложении. У меня есть иерархическая (семейная?) Структура данных, что-то вроде:

{name: "Bob", children: [{name: "Tim", children: [..]}, {another child..}]}

Эта структура может быть многоуровневой.

Теперь я пытаюсь рекурсивно перебрать всех людей, получить их удостоверение личности, сделать вызов API для получения изображения этого человека.

Псевдокод:

gatherPicture(hierarchy);
console.log("I want to wait before doing this!") // Logs too early
function gatherPicture(person) {
    // api request (promise)
    getPicture(person.id).then(r => {
        person.picture = r;
        person.children.forEach(gatherPicture);
    }) 
}

Надеюсь, этот код имеет смысл. Как заставить мой код ждать, пока функция collectPicture не обработает и не разрешит всех людей?

Если это что-то добавляет, я использую AngularJS и у меня есть доступ к сервису обещаний $ q. Но я просто не понимаю, как настроить эту конкретную цепочку обещаний, потому что они построены внутри рекурсивной функции.

Огромное спасибо!

4
Summy 21 Авг 2018 в 16:43

3 ответа

Лучший ответ

Ключ должен использовать $q.all и Array.prototype.map

gatherPicture(hierarchy)
.then(() => {
    console.log("I want to wait before doing this!")
})

function gatherPicture(person) {
    // api request (promise)
    return getPicture(person.id).then(r => {
        person.picture = r;
        return  $q.all(person.children.map(gatherPicture));
    }) 
}

Поэтому сначала верните цепочку Promise, которую вы начинаете с getPicture, из своего gatherPicture, чтобы вы могли создать правильную цепочку Promise.

Затем следующее: вы создаете список Обещаний для всех детей, используя person.children.map(gatherPicture), и затем ждете, пока он не будет решен, используя Promise.all

3
georgeawg 21 Авг 2018 в 14:43

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

const flatten = (tree) => (tree.children || [])
  .reduce((flattened, child) => flattened.concat(flatten(child)), [])
  .concat(tree);

Затем, учитывая простую gatherPicture функцию:

const gatherPicture = (person) => getPicture(person.id).then((picture) => {
  person.picture = picture;
});

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

const gatherPictures = (tree) => Promise.all(flatten(tree).map(gatherPicture));

Использование:

gatherPictures(hierarchy).then(() => {
  console.log("Done");
});
0
sp00m 21 Авг 2018 в 14:20
gatherPicture = (person) => {
    getPicture(person.id)
    .then(data => {
        person.picture = data;
        return Promise.all(person.children.map(gatherPicture));
    }) 
}
0
Chiamaka Ikeanyi 21 Авг 2018 в 14:11
51950132