Hey there and welcome to StackOverflow!
The async keyword in JS only applies to the function it's on. If you use closures (for example in a .map call, or call other functions, those will need to handle Promises themselves or be async, too.
const fetch_data = () => {
return axios
.get("https://swapi.dev/api/people")
.then(res => {
// This return is important!
return people(res.data.results);
})
};
Be aware though, that you need to use async functions or use promises in the other functions that fetch data, too. Therefore I return the people function and change it to be an async function.
You mixed between async functions and .then usage. Usually it's good to stick to using await in async functions. I attached a cleaned up code sandbox at the end.
Side note: a common practice to start an asynchronous task in JS is, to attach the .catch directly to the top-level function call:
fetch_data()
.catch((err) => {
console.log("Ooops!There is an error with fetching the data", err);
});
As I said above, your people function has to be async, so it can fetch data. Since get_movies is async and is called inside the closure in .map, I made the closure async and used Promise.all to make sure fetching the movies for each person runs as expected. Crucially the await get_movies(films) is the important part. An async function returns a promise. To get to the underlying value you want to display, you have to await the result of the get_movies(films) call.
const people = async (data) => {
await Promise.all(data.map(async (person) => {
//create films tags
films = person.films;
let p = document.createElement('p');
let linkText = document.createTextNode(await get_movies(films));
p.appendChild(linkText);
document.body.appendChild(p);
//create names with their info
let mainContainer = document.getElementById("myData");
let div = document.createElement("div");
div.innerHTML = "Name: " + person.name + " Hair Color: " + person.hair_color + " Birth Year:" + " DOB: " + person.birth_year;
mainContainer.appendChild(div);
mainContainer.appendChild(p);
}));
};
Because we .map to create promises, the data fetching happens in parallel. Since the individual calls to axios.get(movie) and therefore get_movies(films) can resolve in any order, the order of the UI elements will also be random. To solve this, fetch the data first, and await all of the results. Then in the next step, iterate over the data to create the nodes.
const people = async (data) => {
const personData = await Promise.all(data.map(async (person) => {
//create films tags
films = person.films;
return { ...person, films: await get_movies(films) };
}))
personData.forEach((person) => {
let p = document.createElement('p');
let linkText = document.createTextNode(person.films);
p.appendChild(linkText);
document.body.appendChild(p);
//create names with their info
let mainContainer = document.getElementById("myData");
let div = document.createElement("div");
div.innerHTML = "Name: " + person.name + " Hair Color: " + person.hair_color + " Birth Year:" + " DOB: " + person.birth_year;
mainContainer.appendChild(div);
mainContainer.appendChild(p);
});
};
With return { ...person, films: await get_movies(films) }; I create a copy of the person object, overwriting films with the result of the fetched films. I do this because we need the properties of person as well as the fetched films in the next step.
The Promise.all ensures that all network requests have finished. And since we used .map the data is in the same order as in the original array.
The get_movies function uses .map but doesn't return anything. If you use curly braces ({) there is no implicit return value, you need to use return explicitly. Just as before you're returning a promise from axios in the .map, which means you get an array of promises. With Promise.all you wait for all promises / network requests to resolve and then convert it to an array of values.
//fetch api from films array
let get_movies = async (movies) => {
return await Promise.all(movies.map(movie => {
return axios
.get(movie)
.then((res) => {
newRes = JSON.stringify(res)
return showMovies(res)
})
}))
}
I took the liberty of cleaning this up a little and putting it in a code sandbox.
Hope this helps. If you have any more questions please don't hesitate to ask.