React native, can't acess elements in array - reactjs

I have a little problem accessing the elements in an array in react native.
I have the problem with the following code:
GetStockPrice = () => {
var stockPrice = StockLogic.getStockPrice();
console.log(stockPrice)
for (let index = 0; index < stockPrice.length; index++) {
console.log(stockPrice[index]);
}
};
The StockLogic.getStockPrice() looks like this:
getStockPrice() {
var stockPricesJSON = [];
this.stocknames.forEach(stock => {
var url =
"https://api.iextrading.com/1.0/stock/" +
stock +
"/batch?types=quote,news,chart&range=1m&last=10";
fetch(url)
.then(resp => resp.json())
.then(data =>
stockPricesJSON.push(
[data.quote.symbol, data.quote.open, data.quote.close, Number(((data.quote.open - data.quote.close)).toFixed(1))]
)
);
});
return stockPricesJSON;
}
If I run the GetStockPrice function, I only get the console.log() of the stockPrice that is passed from the other class. But the logs from the loop don't show up.
I guess that the problem is in the StockLogic, but I can't figure it out.
I hope you can help me once again.
Hope you have a nice day.
Iywern

Because both fetch and forEach are asyncrhonous, stockPricesJSON is being returned as an empty array.
For something like this you'll want to use Promise.all:
function getStockPrice() {
var stockPricesJSON = [];
var promises = this.stocknames.map(stock => {
var url =
"https://api.iextrading.com/1.0/stock/" +
stock +
"/batch?types=quote,news,chart&range=1m&last=10";
return fetch(url);
});
Promise.all(promises).then(function(stocks) {
stocks.forEach(function(stock) {
//...your code
})
})
}
Note that you may need to call Promise.all two times which might look something like this:
Promise.all(promises).then(function(stocks) {
var jsonpArr = stocks.map(function(stock) {
return stock.jsonp();
});
Promise.all(jsonpArr).then(function(stocks) {
stocks.forEach(function(stock) {
//...your code here
});
});
});
And finally instead of returning that value I would suggest you store it with redux or your component state using setState. If this function does not belong to a component then I would suggesting wrapping the entire thing in a promise
new Promise(function(resolve, reject) {
//...your code
})
and calling resolve() on the result
To further improve this I would highly suggest using async/await along with a more modern syntax
const getStockPrice = () => {
return new Promise( async (resolve, reject) => {
try {
let promises = this.stocknames.map(stock => {
var url =
"https://api.iextrading.com/1.0/stock/" +
stock +
"/batch?types=quote,news,chart&range=1m&last=10";
return fetch(url)
})
let result = await Promise.all(promises)
let stocks = await Promise.all(result.map(r => r.jsonp()))
stocks.forEach(stock => {
//...your code
resolve(result)
})
} catch(e) {
reject(e)
}
})
}
And as always, documentation is your friend!
Promise.all()
async / await

I believe this is problem is related to the asynchronous way promises calls are executed. I had a similar problem and I had to run a function like getStockPrice within the response section of your code and assign the result to a state variable.

Related

Making an api call for each value in an array in an async function

I have an array of id's and I am trying to iterate over that array to make an api call for each id present. However my api call doesnt seem to be going through can anyone provide some insight?
the array of id's looks something like this:
["provider-e44f1ccd-839c-413b-9559-0d2b359ee541", "provider-1cd9c1fe-de26-4b44-a1b9-03e6fc103ac4", "provider-1cd9c1fe-de26-4b44-a1b9-03e6fc103ac4", "system", "system", "provider-1cd9c1fe-de26-4b44-a1b9-03e6fc103ac4"]
my function to make the call looks like this :
useEffect(() => {
const loadProvider = async () => {
if(entries.length > 0){
const providerID = entries.map((entry => entry.createdBy))
console.log(providerID)
try{
const results = await api.Providers.get(providerID[+i])
console.log(results, "12345678987654321")
}catch(err){}
}
};
loadProvider();
}, [entries]);
I am able to get inside the try statement but only one call is returned when i console results
I also tried using to map over entries inside the try statement but I got a syntax error for my await result variable.
providerID is an array. You would do well to rename it to something that reflects that. Like providerIDs, for example.
providerID[+i] is a syntax error.
If you want to make an API request for each id, you have to somehow iterate your providerIDs array. You can do that using a forEach:
useEffect(() => {
const loadProvider = () => {
const providerIDs = entries.map((entry) => entry.createdBy);
providerIDs.forEach(async (id) => {
try {
const result = await api.Providers.get(id);
console.log(result);
} catch (error) {
console.log(error);
}
})
};
loadProvider();
}, [entries]);
You can use Promise.all and .map
let results = await Promise.all(entries.map(async (entry) => {
try {
const result = await api.Providers.get(entry.createdBy)
return result;
} catch(error) {
//handle errors
}
}));

How to unite Promises into one Promise in Typescript

I need to return temp_data after it will be fulfilled with data after .map however, now it always returns undefined value as I'm doing this: let temp_data: LooseObject = {}. But without this, I am not able to use LooseObject. Is there any hack that will help me to solve this problem?
async getData(){
let temp_data: LooseObject = {}
this.categorylist.map((category: string) => {
this.getByCategory(category).then((newsList) => {
temp_data[category] = []
newsList.forEach((article: Article) => {
temp_data[category].push(article)
})
console.log('Data: ',temp_data)
})
})
return await temp_data
}
UPDATE:
Here is an example of my getByCategory function:
async getByCategory(category: string) {
const news = await this.newsapi.v2.topHeadlines({
q: category,
sortBy: 'popularity'
})
return await news.articles.map(this._transformArticle)
}
You could solve it by using a reduce function and await Promise.all of the returned array of promises.

using axios with promise API

I am using a promise based hook in a React app to fetch async data from an API.
I am also using a Axios, a promise based http client to call the API.
Is it an anti-pattern to use a promise based client inside another promise? The below code does not seem to work.
const getData = () => {
return new Promise((resolve, reject) => {
const url = "/getData";
axios.get(url)
.then(function(response) {
resolve(response);
})
.catch(function(error) {
reject(error);
});
});
const useAsync = (asyncFunction) => {
const [value, setValue] = useState(null);
const execute = useCallback(() => {
setPending(true);
setValue(null);
setError(null);
return asyncFunction()
.then(response => setValue(response))
.catch(error => setError(error))
.finally(() => setPending(false));
}, [asyncFunction]);
useEffect(() => {
execute();
}, [execute]);
return { execute, pending, value, error };
};
};
const RidesList = () => {
const {
pending,
value,
error,
} = useAsync(getData);
Oh man. I think you have a fundamental misunderstanding about how Promises work.
First, axios already returns a Promise by default. So your whole first function of getData can be reduced to:
const getData = () => {
const url = "/getData"
return axios.get(url)
}
But the meat of your code seems to indicate you want a querable Promise - so you can check the status of it for whatever reason. Here's an example of how you would do it, adapted from this snippet:
function statusPromiseMaker(promise) {
if (promise.isResolved) return promise
let status = {
pending: true,
rejected: false,
fulfilled: false
}
let result = promise.then(
resolvedValue => {
status.fulfilled = true
return resolvedValue
},
rejectedError => {
status.rejected = true
throw rejectedError
}
)
.finally(() => {
status.pending = false
})
result.status = () => status
return result
}
In this way, you can then do something like let thing = statusPromiseMaker(getData()) and if you look up thing.status.pending you'll get true or false etc...
I didn't actually run what's above, I may have forgotten a bracket or two, but hopefully this helps.
I have to admit - I haven't seen anything like this ever used in the wild. I am interested in knowing what you're actually trying to accomplish by this.
Axios itself returns a promise but if you want to make a custom class having your custom logic after each API call then you can use interceptors I was having the same requirement and this is how I am returning promises after applying my custom logic on each API call.
Interceptors will get executed separately after and before each request you made so we can simply use them if we want to modify our request or response.
here is my working solution have a look at it.
callApi = (method, endpoint, params) => {
this.apiHandler.interceptors.request.use((config) => {
config.method = method
config.url = config.baseURL + endpoint
config.params = params
return config
})
return new Promise((resolve, reject) => {
this.apiHandler.interceptors.response.use((config) => {
if (config.status == 200) {
resolve(config.data)
} else {
reject(config.status)
}
// return config
}, error => reject(error))
this.apiHandler()
})
}
Below is the code to call this function
helper.callApi("get", "wo/getAllWorkOrders").then(d => {
console.log(d)
})

How can i use promise.all with forEach in asynchronous function

i have a asynchronous function inside that am using forEach and promise.all. my question is how can i make the function stops until one of the value in forEach is rendered completely and then go for the next one.
sentingMailtoOneRestauarnt this is a large function and now am getting only partial values from this because next value in forEach is invoked before the completion of first one.
exports.handler = async () => {
return fetchAllConnectedAcccounts(null).then((response) => {
var promises_array = []
response.forEach((data) => {
if (data) {
var promise = sentingMailtoOneRestauarnt(data, yesterday).then((resp)=>{
promises_array.push(promise);
})
}
})
return Promise.all(promises_array).then((result) => {
return result;
})
}).catch((err) => {
console.log(err)
});
}
From the code, looks like you already are using async here. Skip ove promises -
const responses = await fetchAllConnectedAcccounts(null);
const results = [];
for (const response of responses){
if (response){
results.push(await sentingMailtoOneRestauarnt(response, yesterday));
}
}
// do whatever with results...
Currently your array is full of resolved promises (you push to it only after promise is resolved).
exports.handler = async () => {
return fetchAllConnectedAcccounts(null).then((response) => {
var promises_array = []
response.forEach((data) => {
if (data) {
var promise = sentingMailtoOneRestauarnt(data, yesterday);
promises_array.push(promise); // push to array before promise is resolved
}
})
return Promise.all(promises_array); // wait for resolving here
/*.then((result) => {
return result;
})*/ // unnecessary
}).catch((err) => {
console.log(err)
});

Running firebase request in a foreach but want to wait for the result to proceed (async/await not working)

I'm using a react useEffect hook and inside that hook I'm fetching data in a foreach from firebase nothing weird here and this seems to be working.
I want to wait for foreach and requests to finish so my array will be build and I can use it in my following code. But it seems my code is not waiting before continuing (the console.log inside the foreach is shown last in my console).
useEffect(() => {
const unsubscribe = firebase
.locations()
.once('value')
.then(async snapshot => {
if (snapshot.val() !== null) {
const locationObject = snapshot.val()
const userInformation = []
await Object.keys(locationObject).forEach(element => {
firebase.user(element).once('value', userSnapshot => {
if (userSnapshot.val() !== null) {
console.log('inside location')
userInformation.push({
userId: 1,
name: userSnapshot.val().username
})
}
})
})
console.log('usrInfo', userInformation)
}
})
})
What am I doing wrong here? Any help would be appreciated!
The Object.keys(locationObject).forEach code doesn't return a Promise, so calling await on it won't do anything.
If you want to wait for multiple promises to resolve, you'll typically want to use Promise.all. I'd also in general recommend using DataSnapshot.forEach() instead of Object.keys().forEach() as it maintains the order of the child nodes.
So combined that'd become something like:
const unsubscribe = firebase
.locations()
.once('value')
.then(async snapshot => {
let promises = [];
snapshot.forEach(element => {
promises.push(firebase.user(element.key).once('value'))
})
return Promise.all(promises);
}).then(snapshots => {
const userInformation = []
snapshots.forEach(userSnapshot => {
if (userSnapshot.exists()) {
userInformation.push({
userId: 1,
name: userSnapshot.val().username
})
}
})
console.log('userInfo', userInformation)
})

Resources