Action in fuction (action for data imported from firstore)
Here you get index data.
export async function getUserIndex (data){
let db = loadFB().firestore();
console.log(data)
let date = moment(data).utc().format()
let query = db.collection('users').where("create_date", ">", date)
console.log(query)
return await query.get().then(docs=>{
let result = docs.size
console.log("result!!", result)
return result
})
}
components in function
async getuserIndex_component(date){
let number =""
number = await userAction.getUserIndex(date)
console.log("number",number)
return await number
}
const {user_list} = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number =0
for (let index_data of data) {
let date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
let index = this.getuserIndex_component(date) //<==here log Promise{<pendding>}
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({ index: index-number, ...index_data });
} else {
number =0;
groups[date] = [{ index: index-number, ...index_data }]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return(...)
I am using redux. in action
Made a "getUserIndex()."
The component will then access the "getUserIndex" and retrieve data. But there is only pending in the console.log, and the data I want does not appear.
In this situation, I would like to know how to deal with async in for of.
I want to know what part I'm making a mistake.
In summary, functions in components are executed later than the render. So the index in the render does not contain the data. What is the solution to this?
As getuserIndex_component is asynchronous, it will return a promise. You will need to wait for that promise to resolve before the index result is available.
Firstly, you should be able to simplify getuserIndex_component to
async getuserIndex_component(date) {
let number = await userAction.getUserIndex(date)
console.log("number", number)
return number
}
And when you remove the console.log it could simply be
async getuserIndex_component(date) {
return userAction.getUserIndex(date)
}
As I mentioned in the comments. You can send off all of the async calls, then just wait for all of the promises to resolve via Promise.all.
Something like this. (I couldn't test any of this, but it should demonstrate the general idea.)
const { user_list } = this.props; //user_list = [{id:aa, pw:bb},{id:cc, pw:dd}...} data
let data = user_list
let groups = {}
let number = 0
const dates = []
const promises = data.map((index_data) => {
const date = moment(index_data.create_date).format("YYYY-MM-DD").slice(0, 10)
dates.push(date) // To use after all promises resolve.
return this.getuserIndex_component(date) // The index promise.
})
const indexes = await Promise.all(promises)
for (let i = 0; i < dates.length; i++) {
const index = indexes[i]
const date = dates[i]
console.log(index)
if (groups[date]) {
let group = groups[date];
group.push({
index: index - number,
...index_data
});
} else {
number = 0;
groups[date] = [{
index: index - number,
...index_data
}]
}
number++;
}
const dates = Object.keys(groups)
const user_list_result = []
for (let date of dates) {
user_list_result.push(...(groups[date]))
}
return (...)
Related
I have such a problem, I am using API call which returns Fake store data, Everything works fine I am pushing received data into arr array, my aim is to call repeatCall() function until arr.length will be arr.length === 100, Also the problem is, sometimes I want to filter received data based on condition and then push it into arr array, What would be the optimal solution in this case?
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
arr.push(...resp)
console.log(arr.length)
}
repeatCall()
Use an if condition on arr.length it will fetch api till the length is equal to 100.
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
arr.push(...resp)
console.log(arr.length)
if(arr.length < 100){
repeatCall();
}
}
As for filtering the data before pushing it to array.
let arr = [];
async function repeatCall() {
const req = await fetch(`https://fakestoreapi.com/products`);
const resp = await req.json();
resp.foreach(item => {
if(condition){
arr.push(item);
}
else{
console.log("don't push")
}
})
if(arr.length < 100){
repeatCall();
}
console.log(arr.length)
}
I am trying to shuffle an array in react . I fetch the data from the api and then I want to mount the data pictures as a shuffled array in my screen and not in the order I fetched them .
This is my code :
useFetch.js
import {React , useState , useEffect} from 'react';
export default function useFetch() {
const [ pokemon,setPokemon] = useState([]);
const [shuffled,setShuffled]= useState([]);
useEffect(()=>{
const fetchPokemon = async () =>{ //here I fetch my pokemon
const promises = [];
for (let i=1;i<=10;i++){
let url = `https://pokeapi.co/api/v2/pokemon/${i}`;
let response = await fetch(url);
let result = await response.json();
promises.push(result);
}
const data = await Promise.all(promises);
setPokemon(...pokemon,data); //successfully sets the pokemon data
}
const shufflePokemon = ()=>{ //here I try to shuffle the pokemon array and return a random on mount
fetchPokemon();
let randomArray= pokemon.map((poke,index)=>{ //this is what I am trying to do to shuffle the array but it is not correct
let j = Math.floor(Math.random() * (index + 1));
let temp = poke[index];
poke[index] = poke[j];
poke[j] = temp;
})
setShuffled(...shuffled,randomArray);
}
shufflePokemon(); //call shuffle on mount
},[])
return {shuffled} //returns shuffled array of objects
}
In my code above in the shufflePokemon function I try to give an idea of what needs to be done but the code is obviously not correct . I would appreciate your help
You can shuffle the array as soon as you get responses from api.
useEffect(() => {
const shuffle = (array) => {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
};
const fetchPokemon = async () => {
//here I fetch my pokemon
const promises = [];
for (let i = 1; i <= 10; i++) {
let url = `https://pokeapi.co/api/v2/pokemon/${i}`;
let response = await fetch(url);
let result = await response.json();
promises.push(result);
}
const data = await Promise.all(promises);
shuffle(data);
setPokemon(data);
};
fetchPokemon();
}, []);
Fischer-Yates shuffle is usually what's used.
It looks like you're close, but the algorithm pulls a random item from the end of the array, not the beginning as you're doing.
const randomArray = Array.from(fetchPokemon()).forEach((v,i,a) => {
const r = a.length - 1 - Math.trunc(Math.random() * i);
[ a[i], a[r] ] = [ a[r], a[i] ];
});
i have some trouble.
I want my function execute when my array is complete.
But the my function execute although my array is not complete.
can you fix my code please..
const historyPermintaanObat2 = obat => {
let array = [];
db.child("itemPermintaan")
.orderByChild("id")
.equalTo(obat.id)
.on("child_added", snapshot => {
let obj = snapshot.val();
let registerPermintaanRef = db.child(
`registerPermintaan/${obj.idPermintaan}`
);
registerPermintaanRef.once("value", snap => {
let username = snap.val().username;
let createdAt = snap.val().createdAt;
let approvedAt = snap.val().approvedAt;
let unit = snap.val().unit;
let obj2 = { ...obj, username, createdAt, approvedAt, unit };
array.push(obj2);
});
});
return array;
};
The result is [] empty array.
If i change the return array.length > 0 && array nothing is happen..
I have figured it out, use once and Promise.all()
const historyPermintaanObat2 = obat => {
db.child("itemPermintaan")
.orderByChild("id")
.equalTo(obat.id)
.once("value", snapshot => {
let array = [];
let obj = Object.values(snapshot.val());
obj.forEach(e => {
let registerPermintaanRef = db.child(
`registerPermintaan/${e.idPermintaan}`
);
let promise = registerPermintaanRef.once("value").then(snap => {
let username = snap.val().username;
let createdAt = snap.val().createdAt;
let approvedAt = snap.val().approvedAt;
let unit = snap.val().unit;
let obj2 = { ...e, username, createdAt, approvedAt, unit };
return obj2;
});
array.push(promise);
});
return Promise.all(array).then(value => {
return value;
});
});
};
Here's the reference i've got Promise.all with Firebase DataSnapshot.forEach
My scenario is as follows:
There is an API I would like to fetch from. The API returns a json that has an array named "assets". This array size will always be 20.
Now, I call the endpoint like so:
fetch(
`https://api.example.io/api/v1/assets/?offset=${offset}`
)
where if offset is 0 it will return the array of the 0 - 20 assets, if the offset is 20 it will return 20 to 40 and so on.
I want to check 1000 items which means I would like to call this fetch 1000/20 = 50 times.
Whenever I call the fetch I would like loop over these 20 items and insert them into my DB.
The problem is that I can't do something like this:
let offset=0;
for(let i = 0; i < 50; i++ {
fetch(
`https://api.example.io/api/v1/assets/?offset=${offset}`
)
for(let j = 0; j < 20; j++){
// Insert into DB
}
offset+=20;
}
Due to JS asynchronous nature. Whenever I try to do this it will call the fetch with the value 0 for offset a couple of times, it won't wait until the nested for loop finishes and than call it for 20 and later 40 and so on...
What is the correct way to achive this behavior?
I've nicked the following from one of my nodeJS repos as it employs async code for solving a very similar problem:
// Throttling is important because you don't want to
// overload the API
const createThrottle = require('async-throttle');
const throttle = createThrottle(2);
// First push all the links into an array using
// the offset
const links = [];
for (let offset = 0; offset < 100; offset += 20) {
links.push(`https://api.example.io/api/v1/assets/?offset=${offset}`);
}
// Next create an array of promises by `map`ing
// over the links using `fetch`.
// Notice I've used throttle here to slow down the hits on
// the API
const promises = links.map(link => throttle(async => () {
const res = await fetch(link);
return await res.json();
}));
// Once all the promises are complete, iterate over their datasets
Promise.all(promises).then(datasets => {
// iterate over datasets
});
Why not use Promise.all?
const promises = [];
for (let i = 0; i < 10; i++) {
promises.push(
fetch(`https://api.example.io/api/v1/assets/?offset=${i}`)
);
}
Promise.all(promises).then( _results => {
// All fetch calls have completed at this point. _results will be in array of your fetch results.
console.log(results);
// Do db insert here
});
One thing you could do is to make a function that is a promise. Within that function, have it execute the fetch and then execute the db insert all in one function (using .then). If you did it this way, the single Promise.all call would handle everything. If you don't, you'll have to loop through the promises again and insert those values into the db. It might look something like this:
const promises = [];
for (let i = 0; i < 10; i++) {
promises.push(fetchAndInsert(i));
}
Promise.all(promises).then( _results => {
console.log(results);
});
function fetchAndInsert(offset) {
return new Promise( (resolve, reject) => {
fetch(`https://api.example.io/api/v1/assets/?offset=${i}`).then (_result => {
// db.insert().then( _result => {
//resolve();
//})
})
})
}
You could use async and await. This should work:
async function fetchExample() {
for (let i = 0; i < 50; i++) {
let fetchedData = await fetch(
`https://api.example.io/api/v1/assets/?offset=${offset}`
);
for(data of fetchedData) {
// Insert into DB
}
offset+=20;
}
}
Instead of for..loop, you could use recursion or Promises.
Recursion::
let insertToDB = function (records, cb) {
if (!records.length) return cb();
let record = records.shift();//assuming records is an array
// Insert record into DB1
insertToDB(records, cb);
};
let loop = function (count, offset, cb) {
if (!count) return cb();
fetch(
`https://api.example.io/api/v1/assets/?offset=${offset}`
)
insertToDB(recordsFromFetch, function () {
offset += 20;
--count;
loop(count, offset, cb)
})
};
loop(50, 0, function () {
console.log("All Done");
})
Promise:: I have not run it, so might be some syntactical error, but you get the idea
let insertToDB = function (record) {
return new Promise(function (resolve, reject) {
// Insert record into DB then call resolve
resolve();
})
};
let fetchPhase = function (offset) {
fetch(
`https://api.example.io/api/v1/assets/?offset=${offset}`
)
let dbAll = [];
for (let j = 0; j < 20; j++) {
// assuming you get records from fetch , pass record to be added to db in the parameter
dbAll.push(insertToDB(records[j]))
}
return Promise.all(dbAll)
};
let all = [];
let offset = 0;
for (let i = 0; i < 50; i++) {
all.push(fetchPhase(i));
}
Promise.all(all)
.then(function () {
console.log("ALL DONe");
})
Promise.all(Array(50).fill(null).map((v,i) => {
const url = `https://api.example.io/api/v1/assets/?offset=${i*20}`;
return fetch(url).then(results => {
for (let result of results) {
// insert into DB
}
);
}).then(() => console.log("done"));
I tried to return the value from a Promise and assign it to a local variable inside the function, but the Promise gets fired at last.
The function (which will get fired in Vue.js mounted()):
getPriceForYesterday(){
let yesterdayUSD = [];
for (let i = 0; i < this.cryptos.length; i++) {
let cryptoName = this.cryptos[i].title;
let timestamp = new Date(this.cryptos[i].purchaseDate);
timestamp.setDate(timestamp.getDate() - 1);
timestamp = timestamp.toISOString();
let priceYesterday = getPriceForTimestamp(cryptoName, timestamp);
Promise.all([priceYesterday]).then((values) => {
console.log("values", values)
yesterdayUSD.push(values[0]);
}).catch(e => console.error(e));
}
console.log("yesterdayUSD", yesterdayUSD);
console.log("yesterdayUSD[0]", yesterdayUSD[0]);
}
Output:
yesterdayUSD []
yesterdayUSD[0] undefined
values [13308.06]
values [1278.69]
I want to use yesterdayUSD to compare it with a local storage value and then return the "result" of the comparison to the vue data.
Maybe you can rewrite this method/function to return a promise, too
getPriceForYesterday(){
return new Promise( resolve => {
let yesterdayUSD = [];
for (let i = 0; i < this.cryptos.length; i++) {
let cryptoName = this.cryptos[i].title;
let timestamp = new Date(this.cryptos[i].purchaseDate);
timestamp.setDate(timestamp.getDate() - 1);
timestamp = timestamp.toISOString();
let priceYesterday = getPriceForTimestamp(cryptoName, timestamp);
Promise.all([priceYesterday])
.then((values) => {
yesterdayUSD.push(values[0]);
})
.then( () => {
resolve(yesterdayUSD)
})
}
})
}
and then use it in the mount() method
mount() {
...
getPriceForYesterday().then( yesterdayUSDArray => {
//do something with this yesterdayUSDArray
})
}
The reason that you're not seeing anything when trying to write out yesterdayUSD is because the promises are executing asynchronously and they're not complete before reaching the lines where you use console.log();
I'm going to assume that you meant to use Promise.All() to wait for all of the promises to complete because that's typically what it is used for. I rewrote your example to populate an array of promises and then wait after they all resolve before writing out the values in yesterdayUSD. That should at least allow you to print out the values after all of the promises complete.
getPriceForYesterday(){
let yesterdayUSD = [];
let promises = [];
for (let i = 0; i < this.cryptos.length; i++) {
let cryptoName = this.cryptos[i].title;
let timestamp = new Date(this.cryptos[i].purchaseDate);
timestamp.setDate(timestamp.getDate() - 1);
timestamp = timestamp.toISOString();
let priceYesterday = getPriceForTimestamp(cryptoName, timestamp)
.then((values) => {
console.log("values", values)
yesterdayUSD.push(values[0]);
})
.catch(e => console.error(e));
promises.push(priceYesterday);
}
Promise.all(promises).finally(() => {
console.log("yesterdayUSD", yesterdayUSD);
console.log("yesterdayUSD[0]", yesterdayUSD[0]);
});
}