Как ждать, чтобы вернуть значение, пока обещание не будет выполнено?

Я пытаюсь написать функцию, которая не возвращает ее значение, пока не будет выполнено обещание внутри функции. Вот упрощенный пример того, что я пытаюсь сделать.

'use strict';

function get(db, id) {
     let p = db.command('GET', 'group:${id}');
     p.then(g => {
         g.memberNames = [];
         g.members.forEach(id => {
             User.get(db, id)
                 .then(profile => g.memberNames.push(profile.name))
                 .catch(err => reject(err));
         });
         return g;
    });
}

Это функция, которая запрашивает идентификатор группы и возвращает данные для этой группы. Попутно он также добавляет имена пользователей в структуру данных, чтобы отображать их имена вместо их идентификатора пользователя. Моя проблема в том, что это работает асинхронно и пропускает обратные вызовы .then. К тому времени, когда он вернет g, ни один из обратных вызовов не был вызван, а g.memberNames все еще пуст. Есть ли способ заставить функцию ждать возврата g, пока не будут вызваны все обратные вызовы?

Я много чего видел об ожидании. Здесь это необходимо? Крайне нежелательно добавлять в мой проект другие библиотеки.


person rangeme    schedule 13.06.2016    source источник
comment
Я думаю, что предпочтительный способ сделать это - передать обратный вызов вашей функции, которая будет вызываться, когда она будет готова.   -  person Rushy Panchal    schedule 14.06.2016
comment
Было бы лучше отказаться от идеи синхронного возврата значения. API вашей базы данных предоставляет обещания, поэтому используйте их как таковые и не пытайтесь превратить их во что-то синхронное. Вы можете написать все, что захотите, придерживаясь шаблона асинхронного / обещания. Это вопрос изменения вашей точки зрения.   -  person trincot    schedule 14.06.2016


Ответы (2)


Поскольку ваша операция по возврату всех имен профилей также является асинхронной, вы должны вернуть обещание, выполненное, когда все другие асинхронные операции завершены (или отклонить, когда одна из них отклонена), выполненные с помощью _ 1_

function get(db, id) {
  let p = db.command('GET', 'group:${id}');
  return p.then(g => {
    return Promise.all(g.members.map(id => {
      // NOTE: id is shadowing the outer function id parameter
      return User.get(db, id).then(profile => profile.name)
    })
  })
}
person Mauricio Poppe    schedule 13.06.2016
comment
Это именно то, что я хочу. Спасибо. - person rangeme; 15.06.2016

Есть ли способ заставить функцию ждать возврата g, пока не будут вызваны все обратные вызовы?

Да, есть. Но вам следует изменить свой образ мышления с «ждать, пока не будут вызваны все обратные вызовы» на «ждать, пока все обещания не будут выполнены». И действительно, функция Promise.all тривиально это делает - он принимает массив обещаний и возвращает новое обещание, которое разрешается с массивом результатов.

В вашем случае это было бы

function get(db, id) {
     return db.command('GET', 'group:${id}').then(g => {
         var promises = g.members.map(id => {
//                                ^^^ never use `forEach`
             return User.get(db, id).then(profile => profile.name);
//           ^^^^^^ a promise for the name of that profile
         });
         //
         return Promise.all(promises).then(names => {
//                                    ^^^^^^^^^^ fulfills with an array of the names
             g.memberNames = names;
             return g;
         });
    });
}
person Bergi    schedule 13.06.2016