how can i run my fetch process with other functions in parallel? - javascript

I`m not familiar with javascript. And actually, I have a basic idea. I want to make some requests and while these requests are continuing, I want to process the incoming data in another function.
That is my fetch function. It's kinda recursive. after 5 requests it will end.
function loadNextContainer() {
var url = "sample_videos/container" + numberContainer;
if(numberContainer>4){
return;
}
fetch(url)
.then(response => response.blob())
.then(data => {
console.log("fetched");
//i ll add the fetched data to array or something else
})
.catch((error) => {
console.error('Error:', error);
});
numberContainer++;
loadNextContainer();
}
And you can imagine that this is the process code.
async function loop1() {
for (let i = 0; i < 5; i++) {
console.log(i);
await null;
}
}
And output:
0
1
2
3
4
fetched
fetched
fetched
fetched
fetched
And this is what i want:
fetched
0
fetched
1
fetched
2
3
fetched
4
fetched
It is not important that they work in order. They just need to work together.

You can use Promise.all(), that should solve your problem, in the code snippet below both the functions fetch1 and fetch2 run simultaneously.
const fetch1 = async() => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1")
const json = await res.json()
return json
}
const fetch2 = async() => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/2")
const json = await res.json()
return json
}
const main = async() => {
const results = await Promise.all([fetch1(), fetch2()])
console.log(results)
}
main().catch(e => console.log(e.message))

Related

How to concat multiple responses and set all response in an array [React JS]

I am doing an API call which is returning IDs and based on number of ids I am doing another call and trying to combine the responses but I am stuck with async issues.
const SearchUser = async () => {
try {
const response = await getSearchUsers();
const ids = response.data?.map((user) => user.userId);
await ids.forEach(async (id) => {
const result = await getUserInfo(id);
setRNOUsers(...result);
// combine result in one state
});
} catch (error) {
setSearching(false);
}
};
useEffect(() => {
SearchUser();
console.log('RNOUsers', RNOUsers); // this is empty and runs even before callng api
}, []);
How can handle this?
You can use Promise.all to wait for all responses, and then set them together with setRNOUsers
const SearchUser = async () => {
try {
const response = await getSearchUsers();
const ids = response.data?.map((user) => user.userId);
const responses = await Promise.all(ids.map(id => getUserInfo(id)))
setRNOUsers(...responses.flatMap(x => x));
} catch (error) {
setSearching(false);
}
};
useEffect(() => {
SearchUser();
console.log('RNOUsers', RNOUsers);
}, []);
Side note, the problem with console.log('RNOUsers', RNOUsers) is setRNOUsers (initialized by useState) is asynchronous. Besides that, your API calls are also asynchronous, so you cannot get values from RNOUsers immediately in useEffect. If you want to see data in that log, you should wait until the state is updated and your component gets re-rendered with your latest data.

How to get data with async func?

Ok, I have async function, which get data about cryptocurrency. And when I load page at first time, data is undefined. But, if I change something in code (for example: set insignificant Enter) data is appears.
How change my code to get data with statr page?
const getData = async () => {
try {
const resCoins = []
for (let coinName of coinsName){
let coin = await axios.get('https://api.coingecko.com/api/v3/coins/' + coinName)
resCoins.push(coin.data)
}
let usd = {symbol: 'usd', market_data: {current_price: {}}}
for (let currency of resCoins){
usd.market_data.current_price[currency.id] = 1 / currency.market_data.current_price.usd
}
resCoins.push(usd)
setCoins(resCoins);
} catch (error) {
console.error(error);
}
};
For is a sync operation. They will not wait for response. So you need to use Promise.all()
const resCoins = await Promise.all(
coinsName.map(
(coinName) =>
axios
.get("https://api.coingecko.com/api/v3/coins/" + coinName)
.then((res) => res.data)
)
)
More Info

Performing an api call on each item in an array and appending the result to a new array

I have an array and I am trying to perform an api call on each item.
Like so -
shoppingData.items.map(item => {
getItemInformation(item.id)
.then(response => response.json())
.then(data => {
JSON.parse(data.text);
});
getItemInformation is my api call -
export async function getItemInformation(id) {
try {
const req = await fetch(`**api url**`);
return await req;
} catch (e) {
console.error(e);
return 'Error';
}
}
However, once I have parsed the data, I would like to append it to a new array. This new array will then be used to render a component later down the page like so -
{newArray?.map((item, index) => (
<ShoppingItem key={index} itemDescription={item.description} itemCatergory={item.catergory} />
))}
Im having issues doing this as I have been trying to do it in a useEffect as ideally I need it to happen when the page renders. Furthermore, I tried having newArray as a state e.g const [newArray, setNewArray] = useState([]) but because I am appending items to an array, setNewArray wouldn't allow me to do this.
use Promise.all
const allItems = await Promise.all(
shoppingData.items.map(
item => getItemInformation(item.id)
.then(response => response.json())
)
);
You could also simplify this a bit by putting all the "asyncy" stuff into your getItemInformation method
const allItems = await Promise.all(
shoppingData.items.map(
item => getItemInformation(item.id)
)
);
and
export async function getItemInformation(id) {
try {
const req = await fetch(`**api url**`);
const json = await req.json();
return json;
} catch (e) {
console.error(e);
return 'Error';
}
}
Live example usiong JSONPlaceholder demo api:
async function getItemInformation(id) {
console.log("getItemInformation",id);
try {
const req = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
const json = await req.json();
return json;
} catch (e) {
console.error(e);
return 'Error';
}
}
(async function testIt() {
const shoppingData = {
items: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
};
const allItems = await Promise.all(
shoppingData.items.map(
item => getItemInformation(item.id)
)
);
console.log(allItems);
})()
You can easily call setNewArray(allItems) in react useEffect using this code (basically where I did consolew.log(allItems) above.
I am not sure how do you append the data to your array while you are calling async function in .map without waiting for the result.
I assume you may be trying something like this:
const parsedData = shoppingData.items.map(item => {
getItemInformation(item.id)
.then(response => response.json())
.then(data => {
JSON.parse(data.text);
});
setNewArray(parsedData);
There are two points to note. Firstly getItemInformation() is called on each item async so the order of parsedItem may not be what your expected. Secondly setNewArray() is called while parsedData's async calls may not finish.
One possible way you can try, if you really want to use .map, is using Promise.all(). Promise.all() resolves when all its promises are resolved:
const getParsedData = async() => {
return Promise.all(shoppingData.items.map(item => getItemInfo.......));
}
const data = await getParsedData();
then append data to your array.
Another simpler solution is using for-loop and await:
for (let i=0;i<shoppingData.items.length;i++){
const parsedItem = await getItemInformation(shoppingData.items[i]).........
newArray.push(parsedItem);
}

For loop with fetch returning empty array

I'm writing a server route that makes api calls.
I need to make two different fetch requests cause I need more info that's coming in the first fetch.
The problem is that I'm declaring a variable out of the promise scope and for some reason, my res.send is not awaiting until the array gets full.
I need to iterate until result 9 (I can't use theDogApi's predefined filters to show nine results!)
if (req.query.name) {
var myRes = [];
fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`)
.then(r => r.json())
.then( data => {
for (let i = 0; i < 8 && i < data.length; i++) {
fetch(`https://api.thedogapi.com/v1/images/${data[i].reference_image_id
}`)
.then(r => r.json())
.then(datos => {
myRes.push({ ...data[i], ...datos });
})
}
})
.then(res.send(myRes))
}
I'll appreciate the help!
You can try using Promise.all to turn your array of fetch calls into an aggregate promise that resolves to an array of responses when all have arrived. If any fail, the whole thing fails (use Promise.allSettled if you don't want all-or-nothing semantics). Don't forget to catch the error.
Although the code doesn't show it, be sure to check response.ok to make sure the request actually succeeded before calling .json(). Throwing an error if !repsonse.ok and handling it in the .catch block is a typical strategy. Writing a wrapper on fetch is not a bad idea to avoid verbosity.
Lastly, note that Array#slice replaces the for loop. For arrays with fewer than 8 elements, it'll slice as many as are available without issue.
// mock everything
const fetch = (() => {
const responses = [
{
json: async () =>
[...Array(10)].map((e, i) => ({reference_image_id: i}))
},
...Array(10)
.fill()
.map((_, i) => ({json: async () => i})),
];
return async () => responses.shift();
})();
const req = {query: {name: "doberman"}};
const key = "foobar";
const res = {send: response => console.log(`sent ${response}`)};
// end mocks
fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`)
.then(response => response.json())
.then(data =>
Promise.all(data.slice(0, 8).map(e =>
fetch(`https://api.thedogapi.com/v1/images/${e.reference_image_id}`)
.then(response => response.json())
))
)
.then(results => res.send(results))
.catch(err => console.error(err))
;
Here is an example of an async function unsing await:
async function fun(queryName, key){
const a = [], p, j = [];
let firstWait = await fetch(`https://api.thedogapi.com/v1/breeds/search?name=${req.query.name}&apikey=${key}`);
let firstJson = await firstWait.json(); // must be an Array
for(let i=0,n=8,j,l=firstJson.length; i<n && i<l; i++){
a.push(fetch('https://api.thedogapi.com/v1/images/'+firstJson[i].reference_image_id));
}
p = await Promise.all(a);
for(let v of p){
j.push(v.json());
}
return Promise.all(j);
}
// assumes req, req.query, req.query.name, and key are already defined
fun(req.query.name, key).then(a=>{
// a is your JSON Array
});
JSON
Here's my hot take: Stop using low-level functions like fetch every time you want to get JSON. This tangles up fetching logic every time we want to get a bit of JSON. Write getJSON once and use it wherever you need JSON -
const getJSON = s =>
fetch(s).then(r => r.json())
const data =
await getJSON("https://path/to/some/data.json")
// ...
URL and URLSearchParams
Another hot take: Stop writing all of your URLs by hand. This tangles URL writing/rewriting with all of your api access logic. We can setup a DogApi endpoint once, with a base url and an apikey -
const DogApi =
withApi("https://api.thedogapi.com/v1", {apikey: "0xdeadbeef"})
And now whenever we need to touch that endpoint, the base url and default params can be inserted for us -
const breed =
// https://api.thedogapi.com/v1/breeds/search?apikey=0xdeadbeef&name=chihuahua
await getJSON(DogApi("/breeds/search", {name}))
// ...
withApi has a simple implementation -
const withApi = (base, defaults) => (pathname, params) =>
{ const u = new URL(url) // <- if you don't know it, learn URL
u.pathname = pathname
setParams(u, defaults)
setParams(u, params)
return u.toString()
}
function setParams (url, params = {})
{ for (const [k,v] of Object.entries(params))
url.searchParams.set(k, v) // <- if you don't know it, learn URLSearchParams
return url
}
fruits of your labor
Now it's dead simple to write functions like imagesForBreed, and any other functions that touch JSON or your DogApi -
async function imagesForBreed (name = "")
{ if (name == "")
return []
const breed =
await getJSON(DogApi("/breeds/search", {name}))
const images =
data.map(v => getJSON(DogAPI(`/images/${v.reference_image_id}`))
return Promise.all(images)
}
And your entire Express handler is reduced to a single line, with no need to touch .then or other laborious API configuration -
async function fooHandler (req, res)
{
res.send(imagesForBreed(req.query.name))
}

Performing Fetch Request in a Loop

I am working on an application that returns a list of ids from first fetch request. After getting the ids, I have to loop through the ids and get details of each item and then display it on the screen.
fetch(TOP_STORIES)
.then(function(response){
return response.json()
}).then(function(storyIds){
// storyIds is [22,33,44,55,66,77,88,99,123,213,342,45456,778,888]
// is this the best way to fetch all the details of the story
storyIds.forEach(function(storyId){
let storyDetailsURL = `https://someurl/v0/item/${storyId}.json?print=pretty`
fetch(storyDetailsURL)
.then((response) => response.json())
.then((story) => {
displayStory(story)
})
})
})
My question is that is looping the best way to get the results?
UPDATE: Promise.all is giving me issues:
UPDATE: Using Async and Await
async function fetchTopHeadlinesAsyncAwait() {
let response = await fetch(TOP_STORIES)
let storyIds = await response.json()
for(let storyId of storyIds) {
console.log(storyId)
let storyDetailsURL = `someurl/er/tg/${storyId}.json?print=pretty`
let response = await fetch(storyDetailsURL)
let story = await response.json()
displayStory(story)
}
}
You can use Promise.all functionality to fetch a list of async actions. They will be completed when all are successful.
Here is example of your code with Promise all, let me know how it works :)
const fetchStories = () => {
let response = await fetch(TOP_STORIES);
let storyIds = await response.json();
let urls = [];
storyIds.forEach(function(storyId) {
urls.push(`https://someurl/v0/item/${storyId}.json?print=pretty`);
});
Promise.all(
urls.map(url =>
fetch(url)
.then(response => response.json())
.catch(err => console.error(err))
)
).then(stories => stories.forEach(story => displayStory(story)));
}

Categories

Resources