0

I want to add a description property to each object in the locations array that is fetched from Wikimedia API but when I log its value inside the loop it is there, but outside the loop, it gets erased.

I looked for solutions with async/await functions or Promise.all() but it didn't work out.

Is there a way to store the value properly to access it later ??

let locations = [
    {
        latLng: [33.975561111111,28.555830555556],
        name: 'Saint Catherine\'s Monastery',
        searchTerm: 'Saint Catherine\'s Monastery',
        urlSerchTerm: 'Saint%20Catherine\'s%20Monastery'
    },
    {
        latLng: [29.91667,31.2],
        name: 'Bibliotheca Alexandrina',
        searchTerm: 'Bibliotheca Alexandrina',
        urlSerchTerm: 'Bibliotheca%20Alexandrina'
    }
];

async function fetchAsync (site, location) {
    // await response of fetch call
    let response = await fetch(site);
    // only proceed once promise is resolved
    let data = await response.json();
    // only proceed once second promise is resolved
    location.description = data[2][0];

    return location.description;
  }

// let fetches = [];
for (let i = 0; i < locations.length; i++) {
    let site = `https://en.wikipedia.org/w/api.php?action=opensearch&search=${locations[i].urlSerchTerm}&limit=1&namespace=0&format=json&origin=*`;

    fetchAsync(site, locations[i])

}
console.log(locations[1].description)

3
  • Where do you actually append the result of fetchAsync into the locations array? Commented Sep 5, 2018 at 11:24
  • 1
    Possible duplicate of How do I return the response from an asynchronous call? Commented Sep 5, 2018 at 11:50
  • @Cloud_Ratha inside the fetchAsync function, the location.description is assigned to the result from the fetch response Commented Sep 5, 2018 at 11:58

2 Answers 2

1

This is simply a timing problem. Your fetch calls are getting executed asynchronously while the console.log(...) statement in the last line of your code snippet is being executed synchronously. In other words, the response to the requests issued by fetch will return after the console.log(...) and the description properties will still be undefined.

You can convince yourself of this by looking at the code below where the console.log(...) statement is wrapped in a timeout. Now the fetched description will be logged instead of undefined.

let locations = [
    {
        latLng: [33.975561111111,28.555830555556],
        name: 'Saint Catherine\'s Monastery',
        searchTerm: 'Saint Catherine\'s Monastery',
        urlSerchTerm: 'Saint%20Catherine\'s%20Monastery'
    },
    {
        latLng: [29.91667,31.2],
        name: 'Bibliotheca Alexandrina',
        searchTerm: 'Bibliotheca Alexandrina',
        urlSerchTerm: 'Bibliotheca%20Alexandrina'
    }
];

async function fetchAsync (site, location) {
    // await response of fetch call
    let response = await fetch(site);
    // only proceed once promise is resolved
    let data = await response.json();
    // only proceed once second promise is resolved
    location.description = data[2][0];

    return location.description;
  }

// let fetches = [];
for (let i = 0; i < locations.length; i++) {
    let site = `https://en.wikipedia.org/w/api.php?action=opensearch&search=${locations[i].urlSerchTerm}&limit=1&namespace=0&format=json&origin=*`;

    fetchAsync(site, locations[i])

}

window.setTimeout(() => {console.log(locations);}, 5000);

You can solve this with Promise.all as suggested by @JeremyThille. This SO answer explains the second use of Promise.all in case this is confusing.

let locations = [
        {
            latLng: [33.975561111111,28.555830555556],
            name: 'Saint Catherine\'s Monastery',
            searchTerm: 'Saint Catherine\'s Monastery',
            urlSerchTerm: 'Saint%20Catherine\'s%20Monastery'
        },
        {
            latLng: [29.91667,31.2],
            name: 'Bibliotheca Alexandrina',
            searchTerm: 'Bibliotheca Alexandrina',
            urlSerchTerm: 'Bibliotheca%20Alexandrina'
        }
    ];


    const fetchDescription = (location) => fetch(`https://en.wikipedia.org/w/api.php?action=opensearch&search=${location.urlSerchTerm}&limit=1&namespace=0&format=json&origin=*`);

    const descriptionRequests = locations.map(fetchDescription);
    Promise.all(descriptionRequests)
    .then(responses => Promise.all(responses.map(r => r.json())))
    .then(descriptions => {
      descriptions.forEach((description, index) => { locations[index].description = description[2][0]; });
    })
    .then(() => {
      console.log(locations);
    });

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. It worked after adding the setTimeout part around the code snippet that is using the description part.
@NouranSamy You're welcome. Do note that I was using a timeout to illustrate the problem and not suggesting that it's a viable solution. You should use Promise.all as suggested in @JeremyThille's answer.
0

Here's my solution with Promise.all :

I'm creating an array of Promises by .mapping over the locations array.

let locations = [
	{
		latLng: [33.975561111111, 28.555830555556],
		name: "Saint Catherine's Monastery",
		searchTerm: "Saint Catherine's Monastery",
		urlSerchTerm: "Saint%20Catherine's%20Monastery"
	},
	{
		latLng: [29.91667, 31.2],
		name: "Bibliotheca Alexandrina",
		searchTerm: "Bibliotheca Alexandrina",
		urlSerchTerm: "Bibliotheca%20Alexandrina"
	}
];

Promise.all(
    locations.map( location => new Promise(async (resolve, reject) => {
        let site = `https://en.wikipedia.org/w/api.php?action=opensearch&search=${location.urlSerchTerm}&limit=1&namespace=0&format=json&origin=*`,
            response = await fetch(site),
            data = await response.json();
        location.description = data[2][0];
        // console.log("Got description = ", location.description)
        resolve();
    })))
.then(() => {
	console.log("locations[1].description = ", locations[1].description);
});

2 Comments

But when adding the console.log statement outside the .then function of the promise, it is undefined. The value is lost outside the promise.
@NouranSamy Yes, because the console.log(...) is being executed before the requests return.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.