JavaScript Asynchronous Programming with JavaScript Understanding Promises Handle Multiple Promises with Promise.all

Soheb Vahora
Soheb Vahora
9,227 Points

TypeError: Cannot read property 'source' of undefined - I think it's one of the astronauts profile picture.

Today is Nov 4th 2020 and there are 3 astronauts in space: Sergey Ryzhikov, Kathleen Rubins, and Sergey Kud-Sverchkov. But I believe there is an error in acquiring Sergey Ryzhikoy's profile, because when I delete the following line:

<img src=${person.thumbnail.source}>

from the generateHTML function, it works.

This is the error: Sergey Ryzhikov - Disambiguation page providing links to topics that could be referred to by the same search term - Sergey Ryzhikov may refer to:Sergey Ryzhikov (cosmonaut), Russian cosmonaut Sergey Ryzhikov (footballer), Russian football player

Below is my code:

const astrosUrl = 'http://api.open-notify.org/astros.json';
const wikiUrl = 'https://en.wikipedia.org/api/rest_v1/page/summary/';
const peopleList = document.getElementById('people');
const btn = document.querySelector('button');

function getJSON(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => {
      if(xhr.status === 200) {
        let data = JSON.parse(xhr.responseText);
        resolve(data);
      } else {
        reject( Error(xhr.statusText) );
      }
    };
    xhr.onerror = () => reject( Error('A network error ocurred') );
    xhr.send();
  });
}

function getProfiles(json) {
  const profiles = json.people.map( person => {
    return getJSON(wikiUrl + person.name);      
  }); 
  return Promise.all(profiles); // returns 6 arrays into 1 with all profiles as individual objects 
}

function generateHTML(data) {
  data.map( person => {
    const section = document.createElement('section');
    peopleList.appendChild(section);
    section.innerHTML = `
      <img src=${person.thumbnail.source}>
      <h2>${person.title}</h2>
      <p>${person.description}</p>
      <p>${person.extract}</p>
    `;
  });
}

btn.addEventListener('click', (event) => {
  getJSON(astrosUrl)
    .then(getProfiles)
    .then(generateHTML)
    .catch( err => console.log(err) )
  event.target.remove();
});

2 Answers

sean shea
sean shea
21,056 Points

Hello Soheb Vahora,

I was having the same issue, so I decided to take the advice from Robert Manolis. You should use .hasOwnProperty() method to check if the object has the specified "thumbnail" property.

function generateHTML(data) {
  data.map( person => {
    const section = document.createElement('section');
    let thumbnail = "";

    if ( person.hasOwnProperty('thumbnail') ) {
        thumbnail = person.thumbnail.source;
    } else {
        thumbnail = "";
    }

    peopleList.appendChild(section);

    section.innerHTML = `
      <img src=${thumbnail}>
      <h2>${person.title}</h2>
      <p>${person.description}</p>
      <p>${person.extract}</p>
    `;
  });
}
Bec Asmar
Bec Asmar
3,083 Points

Great solution! Just something I noticed;

let thumbnail = ""; is superfluous because of

} else { thumbnail = ""; }

you can just put

let thumbnail;

and it will still work

Robert Manolis
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Robert Manolis
Treehouse Guest Teacher

Hi Soheb Vahora, try this:

<img src=${person.picture ? person.picure.thumbnail : ""}>

or

<img src=${data.picture ? data.picure.thumbnail : ""}>

After testing the request and inspecting the data, it looks like you were incorrectly trying to reference the thumbnail image. Hope that helps. :thumbsup:

Soheb Vahora
Soheb Vahora
9,227 Points

With <img src=${person.thumbnail.source}>, the other two images get displayed, but the third one does not. I think it has to do with two individuals having the same name as Sergey Ryzhikov on Wikipedia. Were you able to get this code to work for you? I tried it and I received a TypeError: Cannot read property 'thumbnail' of undefined.

Robert Manolis
seal-mask
.a{fill-rule:evenodd;}techdegree
Robert Manolis
Treehouse Guest Teacher

Hey Soheb Vahora, sorry about that, I was looking at the data returned from just the first URL. I just tested out the complete code, and the reason that it's not working is because data returned for Sergey Ryzhikov doesn't contain a thumbnail property. You can see this for yourself if you log out the data in the generateHTML function, and then inspect the data in the console. So one way to get this to work would be to conditionally generate the HTML template literal, and only add the img element if the person object contains a thumbnail property.

Hope that helps. :thumbsup: