I am trying to get data from a function call and I am keeping it in a while loop so the function will be called again and again if data is not received. What I meant with the data is the data1,data2 and data3 arrays that I have returned from the getData function should be populated and not have a null value. But I am not able to find the solution.
Here is my code :
router.get('/someurl' , async(req,res) => {
let data = [];
while(data.length == 0)
{
data = await functionCall()
console.log(data)
}
})
And here is the format of my functionCall code :
const unirest = require("unirest");
const cheerio = require('cheerio')
const getData = async() => {
const data1 = [] , data2 = [] , data3 = [] ;
try {
const response = await unirest
.get('url')
.headers({'Accept': 'application/json', 'Content-Type': 'application/json'})
.proxy("proxy")
const $ = cheerio.load(response.body)
$('hided').each((i,el) =>
{
data1[i] = $(el)
.find('hided')
.text()
data2[i] = $(el)
.find('hided')
})
$('hided').each((i,el) =>
{
data3[i] = $(el)
.find('hided')
.text()
})
if(data1.length && data2.length && data3.length))
{
return [data1 , data2 , data3]
}
}
catch(error)
{
console.log("Error" , error);
}
return [];
}
module.exports = getData
Firstly remove the if statement, as it is literally what the while loop is doing. then remove await, as this is not in an async function, and it will make an error.
Try something like this.
const fetchData = async ()=>{
data = await functionCall()
if(data.length==0){
fetchData()
}else{
return}
}
Assuming that fetchData is an API call there's no reason to initially define data. You can just log the results of calling that API until the results are an empty array.
const data = [1, 2, 3, 4];
// A simple API that returns a reduced array
// on every call
function mockApi() {
return new Promise(res => {
setTimeout(() => {
res([...data]);
data.shift();
}, 1000);
});
}
// Call the API, and if the the response
// has zero length log "No data" otherwise
// log the data, and call the function again.
async function fetchData() {
const data = await mockApi();
if (!data.length) {
console.log('No data');
} else {
console.log(data);
fetchData();
}
}
fetchData();
Related
So I want to pull data from two different endponts, and send them both to a function at the same time, as the parameters for said function.
function otherFunc(jsonData1, jsondata2){
//do stuff
}
function getJsonData(url1,url2){
fetch(url1);
fetch(url2);
.then((response) => response.json())
.then((data) => otherFunc(data));
}
getJsonData(someUrl1, someOtherUrl2);
So I know how to send one set of data to a function, and I know how to make multiple get requests but I don't know how I can send both sets of jsonData to the same function as params.
Thanks in advance
A little clearer using async/await
function otherFunc(jsonData1, jsonData2) {
//do stuff
}
async function getJsonData(url1, url2) {
const res1 = await fetch(url1);
const res2 = await fetch(url2);
const json1 = await res1.json();
const json2 = await res2.json();
otherFunc(json1, json2);
}
getJsonData(someUrl1, someOtherUrl2);
Alternatively using Promise.all()
function otherFunc(jsonData1, jsonData2) {
//do stuff
}
async function getJsonData(url1, url2) {
const resArr = await Promise.all(
[url1, url2].map(url => fetch(url))
);
const [json1, json2] = await Promise.all(
resArr.map(res => res.json())
);
otherFunc(json1, json2)
}
getJsonData(someUrl1, someOtherUrl2);
or...
function otherFunc(jsonData1, jsonData2) {
//do stuff
}
function getJsonData(url1, url2) {
Promise.all([
fetch(url1),
fetch(url2)
]).then(responses => //map the returned promises to resolve json
Promise.all(responses.map(res => res.json()))
).then(([json1, json2]) => // destructure resolved json
otherFunc(json1, json2)
).catch(error =>
// if there's an error, log it
console.log(error));
}
getJsonData(someUrl1, someOtherUrl2);
function otherFunc(jsonData1, jsondata2){
//do stuff
}
function getJsonData(url1,url2){
fetch(url1).then(res1 => {
fetch(url2).then(res2 => {
// here you have both responses ready
otherFunc(res1.json(), res2.json());
})
})
}
getJsonData(someUrl1, someOtherUrl2);
Solution 2 using async-await:
async function getData(url = '') {
const response = await fetch(url);
return response.json();
}
function otherFunc(jsonData1, jsondata2){
//do stuff
}
async function getJsonData(url1,url2){
const res1 = await getData(url1);
const res2 = await getData(url2);
otherFunc(res1, res2);
}
getJsonData(someUrl1, someOtherUrl2);
I would add the two URLs into an array in getJsonData and loop through them storing the results into an array
function getJSONData(url1,url2){
const urlArr = [url1, url2]
const resultsArr = []
for(let x = 0; x < arr.length){
fetch(arr[x])
}
return resultsArr
}
Inspired by this Wait for multiple promises to finish maybe this can work and avoir having any cascade wait.
function otherFunc(jsonData1, jsondata2){
//do stuff
}
function getJsonData(url1,url2){
var json1, json2;
const promise1 = fetch(url1).then(res1 => { json1 = res1.json() });
const promise2 = = fetch(url2).then(res1 => { json2 = res1.json() });
Promise.all([promise1, promise2]).then((values) => {
otherFunc(values[0], values[1]);
//Or if order of parmeter matter
otherFunc(json1, json2);
});
}
getJsonData(someUrl1, someOtherUrl2);
I have the following code where in the getStuff function I'm making an external async call and storing the result in result. From there I'm looping over result.Content.Ids and pushing the Ids in myArray.
When getStuff() gets called in run(), further down the code I need to access myStuff array but if I console.log it out, it shows up as empty.
const myArray = [];
const getStuff = async (val) => {
const other = {}
if(val) other.newestVal = val;
const result = await someCall(other);
result.Content.Ids.forEach((id) => myArray.push(id));
if (result.AFieldExists) {
getStuff(result.newVal)
}
}
const run = async () => {
await getStuff();
// other code below that uses myArray
// but its empty when I log it
}
run();
I'd have expected myArray to be populated since I'm awaiting getStuff() in run(). What am I doing wrong here?
Your recursive call:
if (result.AFieldExists) {
getStuff(result.newVal)
}
is incorrect, since you're not waiting for the result - only the first getStuff call will be waited for. You need:
const getStuff = async (val) => {
const other = {}
if(val) other.newestVal = val;
const result = await someCall(other);
result.Content.Ids.forEach((id) => myArray.push(id));
if (result.AFieldExists) {
return getStuff(result.newVal)
}
}
You can also clean it up a bit to avoid the ugly outer variable:
const getStuff = async (val, output = []) => {
const other = {}
if (val) other.newestVal = val;
const result = await someCall(other);
output.push(...result.Content.Ids);
return result.AFieldExists
? getStuff(result.newVal, output)
: output;
}
const run = async () => {
const output = await getStuff(); // pass initial value here?
}
you need to put your await calls in try{} catch(){},
and also your recursive call to getStuff() was with out the key word await :
async function someCall() {
return {
Content: {Ids: [1, 2, 3]}
}
}
const myArray = [];
const getStuff = async (val) => {
const other = {}
let result;
if(val) other.newestVal = val;
try{
result = await someCall(other);
}catch(err){
console.log('error from some call' ,err);
}
console.log(myArray)
result.Content.Ids.forEach((id) => myArray.push(id));
console.log(myArray)
if (result.AFieldExists) {
try{
await getStuff(result.newVal)
}catch(err){
console.log('error from get stuff in get stuff' , err);
}
}
}
const run = async () => {
console.log("run")
try{
await getStuff();
}catch(err){
console.log('error' , err);
}
console.log("end run")
console.log(myArray)
// other code below that uses myArray
// but its empty when I log it
}
run();
I am trying to build a React project where I have a component that fetches data from a remote server and plots it.
async function fetchRemoteData(name) {
let dataItem = null;
if (name) {
await fetch("/endpoint/" + name)
.then((res) => res.json())
.then((payload) => {
console.log("payload = " + JSON.stringify(payload));
dataItem = payload;
});
} else {
console.log("Error: No data for " + name);
}
return dataItem;
}
var RenderPlot = ({ match }) => {
let remoteObject = fetchRemoteData(match.params.data);
console.log("remoteObject = " + JSON.stringify(remoteObject));
// .... rendering code
// .... and other irrelevant stuffs to this question
}
If I run this code, on the console, I'm seeing the remoteObject is empty, but the payload inside the function fetchRemoteData() is not.
remoteObject = {}
payload = {"data":"some value"}
Now, let's say I have data stored in the browser (which is obviously I'm not supposed to do, but anyway) and if I fetch it using a generic function call, I'm not having problem.
function fetchLocalData(name) {
// read the data from a local js object
// and return it
}
var RenderPlot = ({ match }) => {
let remoteObject = fetchRemoteData(match.params.data);
console.log("remoteObject = " + JSON.stringify(remoteObject));
let localObject = fetchLocalData(match.params.data);
console.log("localObject = " + JSON.stringify(lovalObject.m))
// .... rendering code
// .... and other irrelevant stuffs to this question
}
and the output --
localObject = 4
remoteObject = {}
payload = {"data":"some value"}
So, what's happening is the code gets the localData from fetchLocalData(), then calls fetchRemoteData() but it doesn't wait if the data is received or not. And then, keeps doing whatever in the rest of the code.
What I want is, the code should wait until the remoteData is received and then proceed to the next steps.
How do I do that?
I can recommend you to store your received data in state of component like example below. If I understood you correctly -
const RenderPlot = ({ match }) => {
const [data, setData] = useState()
const fetchData = useCallback(async name => {
try {
const response = await fetch("/endpoint/" + name);
setData(response.data); // or whatever, depends on how you set your API up.
} catch (e) {
console.log(e)
}
}, [])
// in this case it works as componentDidMount;
useEffect(() => {
;(async function() {
await fetchData(match.params.data);
})()
}, [match.params])
return (<div>...your jsx plot implementation</div>)
}
According to this flow you can compose two fetching,
const fetch1 = useCallback(async () => {your first call api}, [])
const fetch2 = useCallback(async () => {your second call api}, [])
useEffect(() => {
;(async function() {
await fetch1...
await fetch2
})()
}, [match.params])
Or it can be like this
const fetchData = useCallback(name => {
try {
const response1 = await fetch("/endpoint1/" + name);
const response2 = await fetch("/endpoint2/" + name);
// this line would be read after both of your responses come up. Here you can make some operation with both data.
setData(response.data); // or whatever, depends on how you set your API up.
} catch (e) {
console.log(e)
}
}, [])
The reason is that ...return dataItem executes before that promise resolve the request. You need to use async await:
async function fetchRemoteData(name) {
let dataItem = null;
if (name) {
const res = await fetch("/endpoint/" + name);
dataItem = await res.json();
} else {
console.log("Error: No data for " + name);
}
return dataItem;
}
I am trying to load data from firebase by calling a function in which it filters data and returns them.
When I call this function in my main function, it returns "undefined". I know the data is there (console.log(postsArray)) prints the data but I guess the return executes before data is loaded.
What am I doing wrong?
calling_Function_in_Main = async () => {
const data = await FirebaseData ();
console.log(data);
};
FirebaseData is the function that I call in my main function to load data and to return them
let postsArrays=[];
const FirebaseData = async () => {
const getViewableLink = async (link) => { //some function };
const loadData = async () => {
const database = firebase.database();
const data = database.ref();
const loadProfile = data
.child('Posts')
.orderByChild('Active')
.equalTo(true)
.once('value', function gotData(data) {
Object.values(readInfo).forEach(async (element) => {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}
});
})
.catch((error) => {
console.log(error);
}
})
.then((postsArray) => {
console.log(postsArray);
return postsArray;
});
};
await loadData();
};
export default FirebaseSwipeData;
You can't use foreach with async/await because It is not asynchronous. It is blocking.
you have 2 ways to fix this:
1- Reading in sequence: you can use for...of loop
for(const element of Object.values(readInfo)) {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}
2- Reading in parallel: you can use Promise.all
await Promise.all(Object.values(readInfo).map(async (element) => {
element.Option1Link = await getViewableLink(
preLink + element.Option1Link,
);
postsArray.push(element);
}));
Hope that solves the problem, for you
I'm currently trying to loop running a function.
Can't figure it out and here's what I tried:
do {
queryLastCursor(lastCursor).then(lastCursorResults => {
if (lastCursorResults.hasNext = false) {
hasNextPage = false;
}
console.log(hasNextPage);
})
} while (hasNextPage);
queryLastCursor is a method with a call to an API. When it returns the data it would have a value of hasNext if it returns false then I'd like to set hasNextPage to false. The expected behavior would be that it runs the function again and again until we get the result hasNext = false. Any idea of what am I doing wrong?
If you want to do an async process in a loop, I suggest doing it recursively:
const runQuery = () => {
queryLastCursor(lastCursor)
.then(result => {
if (result.hasNext) {
// recursively call itself if hasNext is true
runQuery();
}
});
}
runQuery();
Assuming you'd want to return some data, you can do:
const runQuery = async (data) => {
return queryLastCursor(lastCursor)
.then(result => {
if (!data) {
data = [];
}
// assuming you are returning the data on result.data
data.push(result.data);
if (result.hasNext) {
// recursively call itself if hasNext is true
return runQuery(data);
}
retun data;
});
}
runQuery()
.then(data => {
// data should be an array of all the data now
});
I would do something like that:
const callApi = async () => {
let result = await someMagic();
while (result.hasNext) {
doSomethingwith(result);
result = await someMagic();
}
return result;
};
const myResult = await callApi();
Though this seems risky, what is we always get a hastNext = true ? Some security seems good, like a limit of loops in the while loop.