Ожидание ответа API в цикле

Я перебираю массив и делаю вызов REST API для каждого элемента, но у меня проблемы с асинхронным характером js. Я пытаюсь использовать async/await, но не думаю, что правильно его настраиваю, потому что он не будет ждать ответа и вернет undefined.

onSearchSuccess = async (response) => {
  const persons = response._embedded.persons_search_collection;
  const personsWithClasses = await persons.reduce(
  (acc, person) => {
    const params = {
      person_id: person.person_id,
      date: '2017-01-05',
      enrollment_status: 3,
      class_status: 2,
    };
    return getClasses( //this function does an GET request and returns the response
      params,
      (classesResponse) => {
        const { classes } = classesResponse._embedded;
        console.log(classes); //logs after the console.log below
        return [...acc, { ...person, classes }];
      },
      () => acc,
    );
  }, []);
console.log(personsWithClasses); //return undefined
}


export const getClasses = (params, success, error) => {
  axios.get(`${uri}/classes`, { params })
  .then(({ data }) => {
    success(data);
  })
  .catch(err => error(err));
};

person Matt Aft    schedule 01.03.2017    source источник
comment
Если классы getClasses возвращают обещание, то вы не можете выполнить [...acc], потому что acc будет обещанием. Почему вы используете reduce здесь? Чего точно вы пытаетесь достичь? Вы хотите выполнить все REST последовательно? Или просто ждать, пока все запросы не будут выполнены?   -  person Felix Kling    schedule 01.03.2017
comment
В основном у меня есть множество людей, я делаю запрос, чтобы получить классы для каждого человека, если в этот день есть занятия для этого человека, верните объект как с человеком, так и с классами человека, в противном случае просто верните аккумулятор и двигаться дальше. В основном я хочу отфильтровать людей без классов и вернуть новый объект со свойствами человека и классами, если они есть.   -  person Matt Aft    schedule 01.03.2017
comment
Будет ли полезно, если я предоставлю код для getClasses?   -  person Matt Aft    schedule 01.03.2017
comment
Однако reduce является синхронным. Использование асинхронной функции (что такое getClasses) в качестве обратного вызова reduce не имеет смысла. Вместо этого используйте .map и Promise.all.   -  person Felix Kling    schedule 01.03.2017
comment
хорошо, спасибо, я перепишу это, чтобы использовать promise.all   -  person Matt Aft    schedule 01.03.2017
comment
проблема, с которой я сталкиваюсь с promise.all, заключается в том, что если есть человек без классов, он выдаст отказ и не продолжит   -  person Matt Aft    schedule 02.03.2017
comment
Итак, getClasses возвращает обещание, которое было отклонено?   -  person Felix Kling    schedule 02.03.2017
comment
Я обновил код для getClasses   -  person Matt Aft    schedule 02.03.2017


Ответы (1)


Как я упоминал в комментариях, reduce не будет работать так, как вы хотите, если вы вызываете асинхронные функции. Вы можете использовать Promise.all и .map как таковые (я старался использовать async/await как можно чаще):

onSearchSuccess = async (response) => {
  const persons = response._embedded.persons_search_collection;
  let personsWithClasses = await Promise.all(persons.map(async (person) => {
    try {
      const classes = await getClasses({
        person_id: person.person_id,
        date: '2017-01-05',
        enrollment_status: 3,
        class_status: 2,
      });

      return {...person, classes};
    } catch(error) {
      // ignore errors if a person wasn't found
      return null;
    }
  }));
  personsWithClasses = personsWithClasses.filter(x => x != null);
  console.log(personsWithClasses);
}


export const getClasses = params => {
  return axios.get(`${uri}/classes`, { params });
};

Также обратите внимание на изменения, которые я внес в getClasses. Нет причин принимать обратные вызовы, если axios.get все равно возвращает обещание.

person Felix Kling    schedule 01.03.2017
comment
Благодарность! я забыл обновить код promise.all, который я использовал, но я был близок к этому. Просто не было части try/catch. - person Matt Aft; 02.03.2017
comment
Я действительно пытаюсь заставить эту модель работать, я пробовал так много других вещей, но все потерпели неудачу... одна проблема, с этой частью return {...person, classes}; моя версия не принимает, что classes из переменной, созданной в строке над ним. Почему это происходит? Вот моя версия: let id = response.faceId return { ...imageName, id } идентификатор при его создании выделен серым цветом, он не позволяет использовать идентификатор в операторе возврата. - person Azurespot; 09.10.2019