Fetch не сохраняет значения после массива цикла - Javascript - PullRequest
0 голосов
/ 05 сентября 2018

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

Я искал решения с функциями async / await или Promise.all (), но это не сработало.

Есть ли способ правильно сохранить значение, чтобы получить к нему доступ позже?

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)

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Это просто проблема времени. Ваши fetch вызовы выполняются асинхронно , в то время как оператор console.log(...) в последней строке вашего фрагмента кода выполняется синхронно . Другими словами, ответ на запросы, выданные fetch, вернутся после того, как свойства console.log(...) и свойства description останутся неопределенными.

Вы можете убедиться в этом, взглянув на код ниже, где оператор console.log(...) заключен в тайм-аут. Теперь извлеченное описание будет зарегистрировано вместо 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);

Вы можете решить эту проблему с помощью Promise.all, как предложено @JeremyThille. Этот SO ответ объясняет второе использование Promise.all в случае, если это сбивает с толку.

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);
    });
0 голосов
/ 05 сентября 2018

Вот мое решение с Promise.all:

Я создаю массив Обещаний путем .map пинга по массиву locations.

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);
});
...