@Esm322

Как дождаться загрузки всех данных через Promise.all() и отрендерить их?

Как дождаться подгрузки всех промисов, а затем отренедерить их?
Есть функция рендера страницы со списком данных о фильме:

export function render(data) {
  const planets = data.planets;
  const species = data.species;
  const starships = data.starships;

  const container = document.createElement('div');
  container.classList.add('container', 'pt-4');

  const filmTitle = document.createElement('h1');
  const btnBack = document.createElement('button');
  const filmDescription = document.createElement('p');
  const filmListPlanets = document.createElement('ul');
  const filmListTitlePlanets = document.createElement('h2');
  const filmListSpecies = document.createElement('ul');
  const filmListTitleSpecies = document.createElement('h2')
  const filmListStarships = document.createElement('ul');
  const filmListTitleStarships = document.createElement('h2');

  filmListPlanets.classList.add('list-group');
  filmListSpecies.classList.add('list-group');
  filmListStarships.classList.add('list-group');

  btnBack.classList.add('btn', 'btn-primary');
  btnBack.textContent = 'Back to episodes';
  btnBack.addEventListener('click', () => {
    location = '/index.html';
  })

  filmTitle.textContent = `${data.title} ${data.episode_id}`;
  filmDescription.textContent = data.opening_crawl;
  filmListTitlePlanets.textContent = 'Planets';
  filmListTitleSpecies.textContent = 'Species';
  filmListTitleStarships.textContent = 'Starships';

   Promise.all([
    getDetails(planets),
    getDetails(species),
    getDetails(starships)
  ])
    .then(res => {
      renderDetailsPage2(res[0], filmListPlanets)
      renderDetailsPage2(res[1], filmListSpecies)
      renderDetailsPage2(res[2], filmListStarships)

      container.append(
        btnBack,
        filmTitle,
        filmDescription,
        filmListTitlePlanets,
        filmListPlanets,
        filmListTitleSpecies,
        filmListSpecies,
        filmListTitleStarships,
        filmListStarships,
      )
    })

  return container;
}

Функция getDetails получает массив с url api и выдает промис:

export function getDetails(apiUrl) {
  return new Promise(resolve => {
    const data = apiUrl.map(url => fetch(url)
      .then(res => res.json()))
    resolve(data);
  })
}

Функция renderDetailsPage2:

export function renderDetailsPage2(dataDetails, filmList) {
  return dataDetails.map(detail => {
    detail.then(res => {
      const filmItem = document.createElement('li');
      filmItem.classList.add('list-group-item', 'd-flex', 'flex-column');
      filmItem.textContent = res.name;

      filmList.append(filmItem);
    })
  })
}

В Promise.all я пытаюсь дождаться подгрузки промисов, и затем отрендерить их одновременно со всей страницей. Но страница рендериться последовательно. Сначала основной контент, а затем данные в списках.
Как сделать так, чтобы страница показывалась в тот момент, когда все необходимые данные будут подгружены?
  • Вопрос задан
  • 139 просмотров
Решения вопроса 1
Alexandroppolus
@Alexandroppolus
кодир
Этот твой getDetails возвращает промис, который резолвится массивом промисов. Оный массив не thenable, потому "окончательно резолвится" сразу. Соответственно, Promise.all, получая на вход массив завершенных промисов, тоже не будет чего-то ждать и вернет завершенный промис, значением которого будет массив массивов промисов (находящихся в состоянии загрузки). И только внутри renderDetailsPage2 начинается "поштучная" работа с промисами загрузки и её реальное ожидание.

Переделать надо примерно так:
export function getDetails(apiUrl) {
  return Promise.all(apiUrl.map(url => fetch(url).then(res => res.json())));
}

export function renderDetailsPage2(dataDetails, filmList) {
  return dataDetails.forEach(detail => {
      const filmItem = document.createElement('li');
      filmItem.classList.add('list-group-item', 'd-flex', 'flex-column');
      filmItem.textContent = detail.name;

      filmList.append(filmItem);
  })
}

Теперь getDetails возвращает промис, который резолвится не массивом промисов, а массивом результатов загрузки (т.е. не сразу, а когда все будут загружены).
Promise.all в функции render ждет резолва всех троих, и теперь уже значением его промиса будет массив массивов результатов. Посему в renderDetailsPage2 уже не надо ждать никакого резолва, там в dataDetails - готовый массив значений.

Если что, не дебажил, могут быть мелкие ошибки, но в целом идея вроде бы верная.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Грузите страницу в скрытом состоянии и показывайте после получения ванных. Но учтите, что если что-то будет грузиться медленно, то страница может быть пустой несколько минут.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы