I am trying to use async/await and my code works, I just need advice about is it the right way to use async/await, or do I need .then
here. I have a function loadDataForExport - that return promise with generate data. Also, I have actionButtons with async event function, where I use asyn/await because in other case let data = loadDataForExport() - return promise.
loadDataForExport = async () => {
const {sort, search} = this.state
let dataForExport = await this.props.load({pageSize: this.props.totalCount, skip: 0, sort, search}).then(({entities: {devices, rooms}}) => Object.values(devices).map(({deviceType, deviceId, name, version, location, lastAliveMessage, deviceStatus}) => ({
deviceType,
deviceId,
name,
currentVersion: version,
location: location && rooms[location] ? rooms[location].name : '',
lastAliveMessage,
deviceStatus,
})))
return dataForExport
}
const actionButtons = loadDataForExport => [
{
event: async () => {
let data = await loadDataForExport()
exportToExcel(data)
},
name: 'exportToExcel',,
},
]
From my (too little) understanding of how async/await works, you are doing it the right way. I believe the following is true, feel free to correct me if i'm wrong (I'm ready to pay a few downvotes to get a better understanding of how it works) :
Await will wait (by the means of generators) for the given promise to be resolved before executing the next statement of the async function.
Thus, let dataForExport = await this.props.load(...) will assign the resolved value of this.props.load(...) to dataForExport.
Async function loadDataForExport will return a promise that will either resolve with the value of dataForExport or reject with the value of the rejected this.props.load(), which you can use with await just like you do in loadDataForExport.event()
Related
I am looking to return an array in a promise but it keeps saying promise pending. It is returning promise: fulfilled and promise value: array. I have read the documentation and the thread on here about promises and have tried to use it as a function. I have tried to use async await but nothing seems to be working. I read somewhere that you can store it in a global variable, is that the case. Or is you can't store it in a global variable when you use React, because I am using React. Can someone point me in the right direction?
Here's my code:
const currentSymbol = fetch(`https://finnhub.io/api/v1/stock/symbol?exchange=US&token=${key}`)
.then((res) => {
return res.json();
}).then((results) => {
return results.map(result => {
return result
})
})
console.log(currentSymbol)
How about this beautiful await / async implementation ?
function currentSymbol(){
return new Promise(function(resolve){
fetch(`https://finnhub.io/api/v1/stock/symbol?exchange=US&token=${key}`)
.then((res) => {
resolve( res.json());
})
});
}
Then get result with:
let result = await currentSymbol();
The issue is that you are logging a Promise object which is awaitable. To execute the Promise and get its actual result you need to make use of either .then() or await implementation on the Promise object itself
My 2 API calls happen to be at the same time, where the response of API1 is to be sent as a request parameter to API2. But, the value goes as undefined because it isn't fetched till that time. Is there any way this can be solved in react.
There are multiple ways to solve this problem, I will explain one of the latest as well most sought after ways of solving the problem.
I am sure you would have heard of async/await in JavaScript, if you haven't I would suggest you to go through an MDN document around the topic.
There are 2 keywords here, async && await, let's see each of them one by one.
Async
Adding async before any function means one simple thing, instead of returning normal values, now the function will return a Promise
For example,
async function fetchData() {
return ('some data from fetch call')
}
If you run the above function in your console simply by fetchData(). You'd see that instead of returning the string value, this function interestingly returns a Promise.
So in a nutshell async ensures that the function returns a promise, and wraps non-promises in it.
Await
I am sure by now, you would have guessed why we use keyword await in addition to async, simply because the keyword await makes JavaScript wait until that promise (returned by the async function) settles and returns its result.
Now coming on to how could you use this to solve your issue, follow the below code snippet.
async function getUserData(){
//make first request
let response = await fetch('/api/user.json');
let user = await response.json();
//using data from first request make second request/call
let gitResponse = await fetch(`https://api.github.com/users/${user.name}`)
let githubUser = await gitResponse.json()
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
As you can see the above code is quite easy to read and understand. Also refer to THIS document for more information on async/await keyword in JavaScript.
Asyn/await solves your problem:
const requests = async () =>{
const response1 = await fetch("api1")
const result1 = await response1.json()
// Now you have result from api1 you might use it for body of api2 for exmaple
const response2 = await fetch("api2", {method: "POST", body: result1})
const result2 = await response1.json()
}
If you're using react hooks, you can use a Promise to chain your API calls in useEffect
useEffect(() => {
fetchAPI1().then(fetchAPI2)
}, [])
relevant Dan Abramov
fetch(api1_url).then(response => {
fetch(api2_url, {
method: "POST",
body: response
})
.then(response2 => {
console.log(response2)
})
})
})
.catch(function (error) {
console.log(error)
});
or if using axios
axios.post(api1_url, {
paramName: 'paramValue'
})
.then(response1 => {
axios.post(api12_url, {
paramName: response1.value
})
.then(response2 => {
console.log(response2)
})
})
.catch(function (error) {
console.log(error);
});
Ok so i've searched around and found nothing related to this problem.
My problem is something like this ->
I create an object (push into array) with some info taken from an api. After getting the info from the api i need to call yet another API to get further information on users. Since there are multiple keys for users i'd like to be able to set them inline with a simple function.
I'm doing something like this ->
_item.push({
Author: setPeople(item.Author.Title),
Title: item.Title,
....
Requester: setPeople(item.Requester.Title
})
At the moment i am getting the promise set(entirely) and not the PromiseValue. I know you usually do something like setPeople(name).then(() => {}) however that is not working in my object (sets the key too fast).
Any tip on how i should approach this?
Updating with more code.
export const retrieveIrfItems = async (spId) => {
let spQuery = "SITE" + SpQueryExtend1 + spQueryExpand;
return new Promise((resolve, reject) => {
let _items = [];
axiosApi.get(SiteUrl + spQuery).then((response) => {
//console.log(response.data.d);
return response.data.d;
}).then(async (item) => {
//let requesterSP = setPeople()
const createSPUser = async (user) => {
let spUser;
console.log("User prop is");
console.log(user);
setPeople(user).then((item) => {
spUser = item;
});
return spUser;
}
_item.push({
Author: setPeople(item.Author.Title),
Title: item.Title,
....
Requester: setPeople(item.Requester.Title
})
Ignore the unused function, i'm still doing tests to find a way for this problem.
Found the fix thanks to comments.
Using async/await wouldnt help me since i'd still get promise pending or undefined. What i had to use is ->
Requester: await setPeople(item.Requester.Title).then((user) => { return user }),
Using that in my object seems to work, but my question is...how good is this approach? If there are lots of fields with this behaviour (currently 5), wouldnt that slow down the page by...a lot ?
Then you should try something like that :
export const retrieveIrfItems = async (spId) => {
return new Promise((resolve, reject) => {
let spQuery = "SITE" + SpQueryExtend1 + spQueryExpand;
let _items = [];
try{
const axiosApiItem = await axiosApi.get(SiteUrl + spQuery);
const item = axiosApiItem.data.d;
_item.push({
Author: await setPeople(item.Author.Title),
Title: item.Title,
...
Requester: await setPeople(item.Requester.Title)
});
return resolve(_items);
}catch(e){
return reject(e);
}
});
}
Async / await is a way to consume async Promise functions. The async keyword tells the javascript compiler that this function will consume one or more asynchronous functions. The await keyword tells the compiler that the next function call is asynchronous and returns a promise.
In your code, your first then() function should return a promise which is not the case, that's why the second then() can't be reached.
Also, in your code, your new Promise doesn't return anything. When you create a new Promise, you have to end it by calling resolve() or reject.
Hope it helps...
I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};
I'm really confused about why I can not return the JSON result from amazonMws.products.search() and could use some help understanding what is going on. When I write it this way gives me undefined:
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
amazonMws.products.search(options, (err, res) => {
if(err){
throw(err)
return
}
return res
})
}
I also get undefined when using amazonMws.products.search().then().catch() as well.
If I return amazonMws.products.search() I get a promise back instead of the result.
Inside of the callbacks if I console.log(res) I get back the JSON result I'm expecting. So this led me to believe I need to use async await I think, but this results in Promise { <pending> }:
async function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return await amazonMws.products.search(options)
.then(res => {
return res
})
.catch(e => errorHandler(e))
}
I am totally lost, so if someone could explain to me what is going on, that would be greatly appreciated.
The amazonMws.products.search function is asynchronous, meaning that it will give you a value later, and because of this, you can't get the value now. Instead, you'll have to say what you want to do later when you receive the value.
This is what returning the promise does. The promise itself is the representation of this value that you'll receive later.
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return amazonMws.products.search(options)
}
Then, when calling the function, attach a handler to the promise.
listMatchingProducts(someQuery)
.then(result => {/* do something with result */})
.catch(error => {/* handle the error */})
And, though you don't need to use async await here, it can make the code look a little nicer in some situations, as though it were synchronous. Here's what calling the above function would look like with async await:
async function getProducts() {
try {
const result = await listMatchingProducts(someQuery)
// do something with result
} catch (error) {
// handle the error
}
}
And, as usual, always consult the docs for any detail you're confused about:
Using promises
await keyword
function listMatchingProducts(query) {
const options = {
Version: VERSION,
Action: 'ListMatchingProducts',
MarketplaceId: MARKET_PLACE_ID,
SellerId: SELLER_ID,
Query: query
}
return amazonMws.products.search(options); //returns a promise
}
As others have pointed out, Promises just don’t work the way you think they do.
See my answer to function returning too early before filter and reduce finish for a (hopefully) clear explanation of the problem you face.