How to avoid then when calling a async function [duplicate] - javascript

This question already has answers here:
How can I access the value of a promise?
(14 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 1 year ago.
I often read that async/await is better than .then(). I learned, to use await, I need an async function. To handle errors, I need try/catch. So I built this simple get request:
const isAvailable = async (url) => {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (err) {
console.error(err);
}
};
const fetchedData = isAvailable("/api");
console.log(fetchedData); // Returns: Promise {status: "pending"}
When I see this, it is somehow logical for me, but I can't really understand it.
fetch() returns a Promise, thus fetchedData is a Promise. But the try block shout return an object, thus fetchedData should be a Promise. Even when I see this, it is somehow logical for me, but I can't really understand it, either. fetchedData is a pending Promise and should be awaited as well, so I tried:
const fetchedData = await isAvailable("/api"); //SyntaxError: Unexpected identifier 'isAvailable'. Expected ';' after variable declaration.
console.log(fetchedData);
I'd say this is because I call await without being in an async function, so I ended with:
isAvailable("/api").then((fetchedData) => {
console.log(fetchedData);
});
But now I'm back to .then() what I want to avoid in my exercise.

Related

Why my promise based request returns Promise object on return and expected data in console.log [duplicate]

This question already has answers here:
Async function returning promise, instead of value
(3 answers)
Closed 7 months ago.
please check the below axios get request example to a random public api.
Intro
I would simply like to create a function which will execute get requests and return data, so I can store the returned data in a variable and use it in the UI I am building.
Currently the function returns Promise object and not data as expected.
I also noticed that returned promise object is printed before logged data response. This makes me think that for some reason return command doesn't await the resolution of the promise.
I can use any other library apart axios as well, but tried fetch which is giving me same results.
Furthermore, I tried executing correct code from other stackoverflow answers on this topic, but it gave me same results.
Which leads me thinking my issue has something to do with my environment.
What I already tried
I looked into all answers on query "axios request returns undefined" and similar
I looked into all answers on query "get data from axios request" and similar
tried executing the code from browser using react
changing the function to synchronous, which instead of Promise object return undefined
My env:
System Version: macOS Monterey 12.4 (21F79)
Kernel Version: Darwin 21.5.0
Node#v18.7.0
axios#0.21.4
iterm#2
Example
The example can be executed by running node script.js.
// filename: script.js
const axios = require("axios");
async function fetchApi() {
const getRequest = await axios.get("https://api.publicapis.org/entries")
.then((response) => {
console.log("logged response:", response.data.count);
return response.data.count;
})
};
const response = fetchApi();
console.log("returned response:", response);
// execute#bash: node script.js
// returned response: Promise { <pending> }
// logged response: 1425
My question
I would expect returned result and logged result to be the same.
Please help me troubleshoot this issue.
It is impossible to return the data, because the data doesn't exist yet. Instead, you will need to return a promise. async functions automatically return promises, so you're part way there, but you currently aren't returning anything from your async function. I also recommend you don't mix async/await with .then as it will just cause confusion:
async function fetchApi() {
const response = await axios.get("https://api.publicapis.org/entries")
return response.data.count;
};
To access the eventual value of the promise, you will need to put your code in an async function and await the promise:
async function someFunction() {
const count = await fetchApi();
console.log(count);
}
Your fetchApi is an async function, which means it will return a Promise.
You should use either await or .then() syntax to execute your next actions after the Promise is resolved.
The only problem is that you can't use await syntax in your current implementation since you're using the result in the top level of your script file and not inside a function, Node didn't support top-level awaits in older versions.
So you should either use .then() syntax like this:
fetchApi().then( response => {
console.log("returned response:", response)
};
or wrap your code inside a function if you want to use await syntax:
async function main() {
const response = await fetchApi();
console.log("returned response:", response);
}
main();
if you are using Node v14.8+ you can just use await inside a module:
const response = await fetchApi();
console.log("returned response:", response);

Promise returns after awaiting in map function but data returns when awaiting outside map function [duplicate]

This question already has answers here:
async/await implicitly returns promise?
(5 answers)
Async function returning promise, instead of value
(3 answers)
Closed 1 year ago.
I am fetching userData from MongoDB by passing userId then awaiting for userData. Its returning userData if I request data without an array of ids. it gives me data. but if I run a loop on Array of ids and await that It gives me an array of promises but not an array of user data. why is that happening?.
I know how to solve it by await Promise.all(promiseArray).
but my question is why is this behavior? and also when I try to console the user in the map function it consoles userData, not the promise (on line 156). and I have learned that await stop the execution wait for the line that is awaiting to run then after that i continues. but here its not the case. its not stopping the map function instead if doesn't run code after the await but showing the behavior of continue keyword at await. and then after when the promise of await if fulfilled then code after that runs.
here is the code
// this.guides = ["userIdOne","userIdTwo"]
tourSchema.post("save", async function (doc, next) {
const guideSingle = await User.findById(this.guides[0]);
const guideArray = this.guides.map(async (id) => {
console.log("ID: ", id);
let user = await User.findById(id);
console.log("USER: ", user);
return user;
});
console.log("SINGLE GUIDE: ", guideSingle);
console.log("ARRAY GUIDE:", guideArray);
next();
});
here I am adding output of this code.

saving data from promise in a variable

this is a problem that is going around for DAYS in my team:
We can't figure out how to save the result of a promise (after .then) in a variable.
Code will explain better what I mean:
We start with a simple async function that retrieves the first item of a list:
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
Now, we would like to call this function and save the content of aList:
let aList;
const a = async() => {
aList = await getFirstHelloWorld()
}
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log(aList)
aList called within the .then(), console.logs the right value.
aList called outside the .then(), console.logs Promise { }
How can we save returned values from promises to a variable?
You cannot take an asynchronously retrieved value, stuff it in a higher scoped variable and then try to use it synchronously. The value will not be present yet because the asynchronous result has not been retrieved yet. You're attempting to use the value in the variable before it has been set.
Please remember that await only suspends execution of a local function, it does not stop the caller from running. At the point you do an await, that function is suspended and the async function immediately returns a promise. So, NO amount of await or return gets you anything by a promise out of an async function. The actual value is NEVER returned directly. All async functions return a promise. The caller of that function then must use await or .then() on that promise to get the value.
Try running this code and pay detailed attention to the order of the log statements.
console.log("1");
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
let aList;
const a = async() => {
console.log("beginning of a()");
aList = await getFirstHelloWorld()
console.log("end of a()");
}
console.log("2");
a().then(() => {
console.log('got result:', aList)
console.log('aList is promise:', aList instanceof Promise)
});
console.log("3");
console.log('attempting to use aList value');
console.log(aList)
console.log("4");
That will give you this output:
1
2
beginning of a()
3
attempting to use aList value
undefined
4
end of a()
got result: hi
aList is promise: false
Here you will notice that you are attempting to use the value of aList BEFORE a() has finished running and set the value. You simply can't do that in Javascript asynchronous code, whether you use await or .then().
And, remember that the return value from an async function becomes the resolved value of the promise that all async functions return. The value is not returned directly - even though the code syntax looks that way - that's a unique property of the way async functions work.
Instead, you MUST use the asynchronous value inside the .then() where you know the value is available or immediately after the await where you know the value is available. If you want to return it back to a caller, then you can return it from an async function, but the caller will have to use .then() or await to get the value out of the promise returned from the async function.
Assigning an asynchronously retrieved value to a higher scoped variable is nearly always a programming error in Javascript because nobody wanting to use that higher scoped variable will have any idea when the value is actually valid. Only code within the promise chain knows when the value is actually there.
Here are some other references on the topic:
Why do I need to await an async function when it is not supposedly returning a Promise?
Will async/await block a thread node.js
How to wait for a JavaScript Promise to resolve before resuming function?
Using resolved promise data synchronously
How to Write Your Code
So, hopefully it is clear that you cannot escape an asynchronous result. It can only be used in asynchronous-aware code. You cannot turn an asynchronously retrieved result into something you can use synchronously and you usually should NOT be stuffing an asynchronous result into a higher scoped variable because that will tempt people writing code in this module to attempt to use the variable BEFORE is is available. So, as such, you have to use the value inside a .then() handler who's resolved value has the value you want or after an await in the same function where the await is. No amount of nesting in more async functions let you escape this!
I've heard some people refer to this as asynchronous poison. Any asynchronous operation/value anywhere in a flow of code makes the entire thing asynchronous.
Here's a simplified version of your code that shows returning the value from an async function which will make it the resolved value of the promise the async function returns and then shows using .then() on that promise to get access to the actual value. It also shows using .catch() to catch any errors (which you shouldn't forget).
FYI, since this is not real asynchronous code (it's just synchronous stuff you've wrapped in some promises), it's hard to tell what the real end code should be. We would need to see where the actual asynchronous operation is rather than just this test code.
const helloWorld = () => [{ name: 'hi' }];
async function getFirstHelloWorld() {
const helloWorldList = await helloWorld();
return helloWorldList[0].name;
}
getFirstHelloWorld().then(name => {
// use the name value here
console.log(name);
}).catch(err => {
console.log(err);
});
You need to return the variable like this and use it within the .then callback.
const a = async() => {
const res = await getFirstHelloWorld()
return res
}
a().then((data) => {
console.log('got result:', data)
});

JavaScript returning a promise even though it prints a string to the console [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I'm trying to make a function that returns a string containing a jwt token, the function used in Amplify returns a promise and I can't get my head around promises but after some struggling, I've managed to get my function to get the string I need from the promise and print it to the console but when I then return this string from the function so I can call it from various places the resulting data is now a promise again. No idea what I'm doing wrong.
async function getToken() {
let userData = await Auth.currentAuthenticatedUser().then(result => result.signInUserSession).then(result => result.accessToken).then(result => result.jwtToken);
console.log(userData); // this prints the token perfectly as text to the console
return(userData); // I want this to return the token as a string not a promise
}
console.log(getToken(); // this prints a promise to the console again even though I've got it to a string in the function.
If you have used await inside a function than it will only return a Promise no matter what so you can just use
getToken().then(console.log)
// or
getToken().then(token => console.log(token))
// both are same
since you can not use await outside an async function as a matter of react application just use state such as update the state of application using setState() in the .then of the promise returned. there is no such need of making the function async.
or if you really want the component to be async than just study <Suspense> in react to handle the components that have to fetch data from a network before being displayed
use it like this.
let result = null
getToken().then(token => {
result = token
// Now you can use the result variable
console.log(result)
})
Think I've sussed it now thanks to #tfarmer4 and #Arish Khan. In my head I wanted to get the token as a string variable so I could pass it into my API call functions but I realise now I will need to call it from within each function so below is my example solution.
function getToken() {
return Auth.currentAuthenticatedUser().then(result => result.signInUserSession).then(result => result.accessToken).then(result => result.jwtToken);
}
function callAPI () {
getToken().then(data => {
let token = data;
console.log(token);
//more lines here such as calling my API using token as the variable of the jwt token
}
);
};
EDIT: Sandbox here All you need is this edit I believe. Remember that async functions are just promises. You have to do any work on the result by either setting the result to a variable with an let data = await Auth.currentAuthenticatedUser().then(result => result).then(data => data.jwtToken), or just do all the necessary work in the .then(data => {data.jwtToken //...your work here}).

Single async function that makes multiple fetches and returns results? [duplicate]

This question already has answers here:
Why does .json() return a promise?
(6 answers)
Closed 4 years ago.
I am attempting to write an async function that makes multiple fetches, waits for all to complete, then returns the JSON results as an array. This is what I have so far:
file1.js
const searchResults = await Api.search(searchText);
api.js
async function search(searchText) {
return util.executeFetchAll([
`/searchA/${searchText}`,
`/searchB/${searchText}`,
`/searchC/${searchText}`
]);
}
util.js
async function executeFetchAll(urls) {
const promises = urls.map(url => fetch(url));
const responses = await Promise.all(promises);
debugger;
}
When execution pauses at the debugger and I inspect responses using Chrome's dev tools, it is correctly an array of 3 Response objects, but if I inspect responses[0].json(), it weirdly returns in the console a Promise {<pending>} object.
What am I missing? I'm awaiting Promise.all, which should mean all promises resolve before my debugger line. So why is the json() method weirdly showing a Promise object in a pending state?
Thanks.
response.json() return a Promise! see: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
So you can await them with something like this:
await Promise.all(responses.map(r => r.json()));

Categories

Resources