問題描述
如何在 .map 中返回異步函數? (How do I make an asynchronous function return in .map?)
我試圖在 fetchPersons
函數的其餘部分執行之前讓 fetchIDFromPerson
函數返回並將返回的值分配給 ID
。它只返回未定義,因為它是異步的。我該怎麼做才能讓它工作?謝謝。
const fetchIDFromPerson = name => {
axios
.get(`https://swapi.co/api/people/?search=${name}&format=json`)
.then(response => extractImgIDFromURL(response.data.results[0].url))
.catch(error => console.error(error));
};
let body;
const fetchPersons = (query, imgID) => {
SWAPIGraphQL.post("", { query })
.then(result => {
const { allPersons } = result.data.data;
return (body = allPersons.map(person => {
const ID = fetchIDFromPerson(person.name); //This returns undefined!
return {
...person,
imgID: ID //needs to be assigned a value from fetchIDFromPerson
};
}));
})
.catch(error => console.error(error));
};
fetchPersons(GET_PERSONS_DATA);
參考解法
方法 1:
You need to make a number of changes to properly use the promises that your asynchronous code return:
return
the axios promise to the caller infetchIDFromPerson()
.- rethrow error in
.catch()
after logging it so the caller sees the error return
thereturn SWAPIGraphQL.post()
promise to the caller infetchPersons()
.- Get rid of the
body
variable. You don't want side effect programming, particular with asynchronous code. Communicate back results through returned promises. - Use
Promise.all()
on the results of the.map()
so you know when all those promises are done. - When calling
fetchPersons()
use the returned promise to get the resolved value. - Inside the
.map()
return the promise fromfetchIDFromPerson()
. - Add a
.then()
to where you callfetchIDFromPerson()
where you can return the combined data structure with person and ID in it. - Change
response.data.results[0].url
toresponse.results[0].url
to match the data structure you're getting back.
Here's the fixed up code:
const fetchIDFromPerson = name => {
return axios
.get(`https://swapi.co/api/people/?search=${name}&format=json`)
.then(response => extractImgIDFromURL(response.results[0].url))
.catch(error => {
// log and rethrow
console.error(error);
throw error;
});
};
const fetchPersons = (query, imgID) => {
return SWAPIGraphQL.post("", { query })
.then(result => {
const { allPersons } = result.data.data;
return Promise.all(allPersons.map(person => {
return fetchIDFromPerson(person.name).then(ID => {
return {
...person,
imgID: ID
};
});
}));
})
.catch(error => {
// log error and rethrow
console.error(error);
throw error;
});
};
fetchPersons(GET_PERSONS_DATA).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
You can simplify fetchPersons()
a bit by using async/await
and not logging the error as many separate places:
const fetchPersons = async (query, imgID) => {
const result = await SWAPIGraphQL.post("", { query });
const { allPersons } = result.data.data;
return Promise.all(allPersons.map(async (person) => {
const ID = await fetchIDFromPerson(person.name);
return {
...person,
imgID: ID
};
}));
};
(by Aleksandar Zdravkovski、jfriend00)