I collect some data from various urls into an array, using a for loop:
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i ) {
const promise = getPhotosData(i);
promise.then((items) => {
data.push(items);
});
data3 = data;
}
if I log data3
to the console, I'll get an array with multiple arrays of 50 objects each, but if I log data3.length
I get 0, and of course i can't perform any iterations on it like, map
, flat
or forEach
(what i want is to flatten data3 into a single array of objects).
I tried defining data3
as a state variable and then only flatten it (or even just get its's length), after setting it's value inside a useEffect
hook, with data
inside dependency array (or dependency array empty), but got same results.
CodePudding user response:
You need to use Promise.all in order to wait for all promises to finish, and then assign the result to your data3
variable.
Then, use flat()
and you'll get the result you want.
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let promises = [];
for (let i = 1; i < 10; i ) {
promises.push(getPhotosData(i));
}
Promise.all(promises).then((values) => {
data3 = values;
console.log(data3.length, data3.flat().length);
});
CodePudding user response:
The problem is that javascript does not wait for asynchronous code to complete before going further in execution.
Reading or watching videos on code execution in javascript (event loop, call stack) will help a lot.
In this case, you're not waiting for the promise to be fulfilled and then console.log - so your code is putting out a length of 0 for data3 because the promises are not resolved yet(the API calls haven't been finished and therefore data.push(items);
this code hasn't run yet).
So one way to solve this is your exact same code but you need to put an await to promise.then as shown here
const getData = async () => {
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i ) {
const promise = getPhotosData(i);
await promise.then((items) => {
data.push(items);
});
data3 = data;
}
console.log("data3 - ", data3);
};
getData()
If however you're using this in react.js context and want the data to be loaded and then use it in the page
import { useEffect, useState } from "react";
import "./styles.css";
const getData = async () => {
let data3;
const getPhotosData = async (album) => {
const url = `https://jsonplaceholder.typicode.com/albums/${album}/photos`;
const res = await fetch(url);
const jsonRes = await res.json();
return jsonRes;
};
let data = [];
for (let i = 1; i < 101; i ) {
const promise = getPhotosData(i);
await promise.then((items) => {
data.push(items);
});
data3 = data;
}
return data3;
};
export default function App() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const dataResult = await getData();
setData(dataResult);
};
fetchData();
}, []);
if (data.length === 0) return <div>...loading</div>;
return (
<div className="App">
<pre>{JSON.stringify(data, null, 3)}</pre>
</div>
);
}
Keep in mind to read about promises and async/await which is basically a sugar syntax of promises. Also your code could be optimized but that's beyond the scope of this question. Working example of your code at codesandbox