Async/Await variable affectation in javascript for loop - javascript

I would like to achieve the following thing in JS but I've got the feeling that I don't have the right approach.
I'm doing a first request giving me a list of objects
For each object I'm doing another request to an API to get matching values
Apply a specific logic to the result
Finally call a function that uses the data provided by the API and modified
My code looks like this :
async function processData(){
const models = await fetch('/api/models').then(response => response.json());
for(let m in models){
for(let i of models[m].items){
if(i.type == 1){
i.values = await fetch('/api/main/'+i.id).then(response => response.json());
}
else{
i.values = await fetch('/api/custom/'+i.id).then(response => response.json());
// + additionnal specific code on values
}
console.log(i.values);//<--- NOT WHAT EXPECTED
}
display(models[m]);
}
}
My problem is that display function is called before i.values is updated.
I think the whole for(let i models[m].items){} loop should be asynchronous and the display function should be called only once all fetch requests for each i item have been resolved.
But I'm struggling to write the code and I'm not sure this is the good way of doing this.

Related

make for loop wait till data is back from API

I have a structure like this:
methods: {
function() {
//some stuff in here
for(let i=0; i < array.length; i++) {
//Problem in here or around here
//in here I send something to my API and want to get data back
}
}
}
Explenation what I do: I have an array where I want to loop, for every loop I send data to my API and get data back inside the for-loop.
My problem: I always get the data back in different order because one ot the data is probably bigger and than it takes longer than the others.
Example: I send Array = [1,2,3,4] and want to get it back in the correct order. But 1 needs more time than 2,3,4 and I get than data back in order [2,3,4,1].
How can I assign my for-loop to wait till the first data is back and than go on?
Thank You!
As soon as you've read up on async/await, you'll still wanna run your requests in parallel to save time, rather than sequentially. Promise.all helps here:
async function fetchData () {
const resultsInOrder = await Promise.all(
// pass an array of Promises
array.map(element =>
fetch(/* some arguments based on the element's data */)
.then(res => res.json())
)
)
// do something with results
}

Messy order on items fetched

I'm trying to display 10 pokemons on the html... What I'm getting sometimes is a messy order on the final result. Like pokemon with id=5 appears 2nd and so on .
I assume my error comes from some asynchronism between the functions calls.
I'm kinda new on async functions so don't judge me bad on it lol...
So my code goes like this:
pokemonAPI = "https://pokeapi.co/api/v2/pokemon?offset=0&limit=10";
async function getPokemonList(url){
let resp = await fetch(url);
if (resp.ok){
let list = await resp.json();
list.results.forEach(pokemon => {
getPokemonData(pokemon.url);
});
}
}
Then getPokemonData function:
async function getPokemonData (url){
let resp = await fetch(url);
if (!resp.ok){
throw new Error(`HTTP error!, fetching pokemon data. Status: ${resp.status}`);
}
let data = await resp.json();
fillContent(data);
}
fillContent function uses innerHTML to add a new card to my container div element.
Finally the result (not always) looks like this: Display error
Any suggestions on where I'm getting it wrong?
Thanks to #A_A for the help
Nvm the first comment, I was a bit confused. A simple for loop instead
of forEach and await getPokemonData(pokemon.url) should do the trick
So actually I was calling my function getPokemonData(pokemon.url) one after another in a forEach without waiting for it to finish fetching the url I was sending.
for (let pokemon of list.results){
await getPokemonData(pokemon.url);
}
Changing it to a simple loop and waiting for my function to end solved it

How to use Promise.all with multiple Firestore queries

I know there are similar questions to this on stack overflow but thus far none have been able to help me get my code working.
I have a function that takes an id, and makes a call to firebase firestore to get all the documents in a "feedItems" collection. Each document contains two fields, a timestamp and a post ID. The function returns an array with each post object. This part of the code (getFeedItems below) works as expected.
The problem occurs in the next step. Once I have the array of post ID's, I then loop over the array and make a firestore query for each one, to get the actual post information. I know these queries are asynchronous, so I use Promise.all to wait for each promise to resolve before using the final array of post information.
However, I continue to receive "undefined" as a result of these looped queries. Why?
const useUpdateFeed = (uid) => {
const [feed, setFeed] = useState([]);
useEffect(() => {
// getFeedItems returns an array of postIDs, and works as expected
async function getFeedItems(uid) {
const docRef = firestore
.collection("feeds")
.doc(uid)
.collection("feedItems");
const doc = await docRef.get();
const feedItems = [];
doc.forEach((item) => {
feedItems.push({
...item.data(),
id: item.id,
});
});
return feedItems;
}
// getPosts is meant to take the array of post IDs, and return an array of the post objects
async function getPosts(items) {
console.log(items)
const promises = [];
items.forEach((item) => {
const promise = firestore.collection("posts").doc(item.id).get();
promises.push(promise);
});
const posts = [];
await Promise.all(promises).then((results) => {
results.forEach((result) => {
const post = result.data();
console.log(post); // this continues to log as "undefined". Why?
posts.push(post);
});
});
return posts;
}
(async () => {
if (uid) {
const feedItems = await getFeedItems(uid);
const posts = await getPosts(feedItems);
setFeed(posts);
}
})();
}, []);
return feed; // The final result is an array with a single "undefined" element
};
There are few things I have already verified on my own:
My firestore queries work as expected when done one at a time (so there are not any bugs with the query structures themselves).
This is a custom hook for React. I don't think my use of useState/useEffect is having any issue here, and I have tested the implementation of this hook with mock data.
EDIT: A console.log() of items was requested and has been added to the code snippet. I can confirm that the firestore documents that I am trying to access do exist, and have been successfully retrieved when called in individual queries (not in a loop).
Also, for simplicity the collection on Firestore currently only includes one post (with an ID of "ANkRFz2L7WQzA3ehcpDz", which can be seen in the console log output below.
EDIT TWO: To make the output clearer I have pasted it as an image below.
Turns out, this was human error. Looking at the console log output I realised there is a space in front of the document ID. Removing that on the backend made my code work.

Catching part of a JSON response and saving into a variable?

I have a question I've spent the past few days with my friend google trying to answer. This is the code from a project I'm currently working on and I'm trying to interface with two API's.
What you see here is a call to the first API using the GOT library formatting to receive a JSON response.
var products
//Printify call for products list
(async () => {
try{
const list = await redd('shops/shopId/products.json');
//Catch the Data array and save it into the variable products
var obj = new JSONObject(response);
products = obj.getJSONArray("data");
}
catch(error) {
}
})();
//Print the variable products to the console
console.log(products)
I create a new JSONObject from the response and grab the Data Array from that response and put it in the Variable products which was defined outside this function. Finally I'm trying to print the variable to the console.
Eventually I will need to take that "data" Array and parse for specific items inside it (i.e. title: , description: , images:) and pass it as a value into the next API.
Currently I'm getting a "undefined" response from the console. Not sure what I'm doing wrong, hoping that I can get some help or direction. Anything is appreciated, thank you all in advance!
I was finally able to get it all working. It's actually been a minute since I solved, but wanted to make sure it worked. Here is the final code that ended up working for me:
//Config for a new GOT instance, to use redd as the variable with the attached headers and options
const redd = got.extend({
prefixUrl: 'https://api.printify.com/v1',
responseType: 'json',
headers: {
'Authorization': 'Bearer ' + apiKey
}
});
var productsParsed //Setting up global variable to accept the array from the JSON response body
//Printify call for products list
getProducts = async () => {
try{
const response = await redd('shops/' + shopId + '/products.json');
productsParsed = response.body.data; //Data is the array in the body I need access to, and saving that into the var productsParsed
//Returning the var with the new value
return productsParsed
}
catch(error) {
console.log(error.response);
}
};
getProducts().then(console.log); //Printing the return value from getProducts() which verifies the var contains the needed value
So the solution ended up being fairly simple. I just didn't fully understand the GOT structure. I had to use dot notation to pin down my return statement. After figuring that out I was able to use my global variable that I had set up to accept the value of the JSON response body. Thank you all for your suggestions and assistance. I hope this post is able to help any others in a similar situation to myself.
You've defined an async function, but you haven't awaited its result.
I think adding await in front of the call site would fix that.
Because it's an async function the console.log is called before the async function is finished. Move it inside the async function and it should work.
var products
//Printify call for products list
(async () => {
try{
const list = await redd('shops/shopId/products.json'); // we wait for the response here
//Catch the Data array and save it into the variable products
var obj = new JSONObject(response);
products = obj.getJSONArray("data");
//Print the variable products to the console will work here
console.log(products)
}
catch(error) {
}
})();

How can I make a request to multiple URLs and parse the results from each page?

I'm using the popular npm package cheerio with request to retrieve some table data.
Whilst I can retrieve and parse the table from a single page easily, I'd like to loop over / process multiple pages.
I have tried wrapping inside loops / various utilities offers by the async package but can't figure this one out. In most cases, node runs out of memory.
current code:
const cheerio = require('cheerio');
const axios = require("axios");
var url = someUrl;
const getData = async url => {
try {
const response = await axios.get(url);
const data = response.data;
const $ = cheerio.load(data);
const announcement = $(`#someId`).each(function(i, elm) {
console.log($(this).text())
})
} catch (error) {
console.log(error);
}
};
getData(url); //<--- Would like to give an array here to fetch from multiple urls / pages
My current approach, after trying loops, is to wrap this inside another function with a callback param. However no success yet and is getting quite messy.
What is the best way to feed an array to this function?
Assuming you want to do them one at a time:
; (async() => {
for(let url of urls){
await getData(url)
}
})()
Have you tried using Promise.all (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)?
For loops are usually a bad idea when dealing with asynchronous calls. It depends how many calls you want to make but I believe this could be enough. I would use an array of promises that fetch the data and map over the results to do the parsing.

Categories

Resources