I have the following code where I am calling an api
const fetchToken = async () => {
const response = await axios.post("http://localhost:3000/api/getRedisData",
{
key: process.env.AUTH_TOKEN_NAME!,
});
console.log(response); // this returns what I expect it to return
return response;
};
Then I am calling the function like this.
fetchToken();
Now, this function works just fine. But when I try to save the data of this response like the following.
token = fetchToken().then((r) => r);
It returns a promise and it never resolves. What is happening and how do I wait until this promise resolves. I just need to wait and get the data. Thanks in advance.
I updated this question.
Let's suppose I have the following object.
const authMachine = createMachine<toggleAuth>({
id: "auth",
initial: "unauthenticated", // I want to chage this value based on token
context: {
//
},
states: {
//
},
});
Now I want to update the initial property based on if I am getting a token or not. Right now I am doing something like this.
initial: token ? "authenticated" : "unauthenticated"
So what's wrong here?
If you are in an async function, you can just await it to get the data.
const token = await fetchToken();
console.log(token);
// do other stuff with token
If you aren't, then you need to do everything you want done with the token in your then method. This is because you are calling an async method and without the ability to await it. The execution will just continue. The then function is the callback that happens when your async method completes successfully.
fetchToken().then(token => {
console.log(token)
// do other stuff with token
});
All async functions return a promise. So your fetchToken() function will always return a promise. The resolved value of that promise will be whatever value you return from the fetchToken() function body.
So, the caller of fetchToken() has to use await or .then() to get that resolved value. There is no free lunch with async/await. It's still asynchronous. await gives you synchronous-like behavior inside the function, but not outside the function.
To explain a little further. As fetchToken() executes, as soon as it hits the first await, it immediately suspends further execution of the function body and then immediately returns an unresolved promise back to the caller. The caller then continues to execute and the caller has to use .then() or await to know when the body of fetchToken() is actually done and what its final returned value is.
Then, sometime later, the promises you used await on inside of fetchToken() will resolve or reject and when the JS interpreter is back to the event loop, then it will continue executing the rest of the body of fetchToken() after the await. When it finally gets to the end of the function body or encounters a return statement, then it resolves the promise that was previously returned and the caller that is using either await or .then() will get notified that it is done and will be given the final return value as the resolved value of that promise. The caller can then process that final value and do its thing.
So, you probably want to be using something like this to get the final value or the error:
fetchToken().then(token => {
console.log(token);
// use the token value here
}).catch(err => {
console.log(err);
});
// you cannot use the token value here
If the call to fetchToken() itself is inside an async function, then it can use await also:
try {
let token = await fetchToken();
console.log(token);
// use the token value here
} catch(err) {
console.log(err);
}
Related
After watching the video, and reading in [Promise.then][1] i still don't understand how it works.
So i wrote an example, and printed to console but still with the output i can't understand how does it work.
I hope my question are clear enough, if not please tell me and i will try to elaborate them according to your responses.
This is my code:
const getCountryData = function (country) {
fetch(`https://restcountries.eu/rest/v2/name/${country}`)
.then(response => {
console.log('111111111111111');
if (!response.ok)
throw new Error(`Country not found (${response.status})`);
return response.json();
})
.then(data => {
console.log('22222222222');
const neighbour = data[0].borders[0];
if (!neighbour) return;
return fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
})
.then(response => {
console.log('333333333')
if (!response.ok)
throw new Error(`Country not found (${response.status})`);
return response.json();
})
.then(data => console.log('44444444444'))
.catch(err => {
console.error(`${err} 💥💥💥`);
})
.finally(() => {
console.log('finalyyyy')
});
console.log("after all async calls");
};
I can't understand the following things:
If we look for example on the first then, it gets a callback as a parameter, the callback returns a Promise object. Which object calls the second then? The one that was returned from the first call to when or the one that have been returned from the callback?
I understand that the callback is "attached" to the fetch function. e.g when the fetch function will be finished, the callback will be executed. But i can't understand how and when the second, third and fourth then are called. The first callback contains also an async function ** response.json()**, so how and when exactly the second when will be started if he need the json.response from the first when?
If an exception occur at the first or second, how exactly the code knows to jump to catch function?
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
The then functions don't receive a callback, they receive the result of the previous async operation.
So the result of the fetch is passed to the first then, the result of response.json() is passed to the second then and so on...
In case of error the chain broke and it goes directly to the firs catch function.
Under the hood there are more, consider that the fetch and other operations in the then functions can be asynchronous, so what happens in the runtime is that the result of the fetch function is pushed in the event queue and when the event loop takes your next then function is extracted from the queue and passed to the function.
This article explain how it works internally: https://www.digitalocean.com/community/tutorials/understanding-the-event-loop-callbacks-promises-and-async-await-in-javascript
I will try to explain this in a different way, using async/await operators.
The equivalent of your getCountryData function is as follows
const getCountryData = async (country) => {
console.log("after all async calls"); // explanation for the this line below
try {
let response1 = await fetch(`https://restcountries.eu/rest/v2/name/${country}`);
// first then
console.log('111111111111111');
if (!response1.ok)
throw new Error(`Country not found (${response1.status})`);
let data1 = await response1.json();
// second then
console.log('22222222222');
const neighbour = data1[0].borders[0];
let response2;
if (neighbour)
response2 = await fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
// third then
console.log('333333333');
if (!response2.ok)
throw new Error(`Country not found (${response2.status})`);
let data2 = await response2.json();
// fourth then
console.log('44444444444');
} catch (err) {
console.error(`${err} 💥💥💥`);
} finally {
console.log('finalyyyy');
}
}
First of all, you will see the console.log in your last line of function became the first line in my function. This is because the line is executed before the asynchronous function fetch completes.
As you can see, the last return variable from the fetch is available in the first then, and the last return variable from the then is available in the next then.
So for your first question, who calls the callback function in the next then? Obviously you can see from the async/await equivalent of your function, it is executed in sequence. The return result from the previous then will be the argument for the next then. Therefore in the second then, when you do return;, it will not stop executing the third then, but instead the third then will reveive undefined as the response argument.
As explained in answer to question 1, the then functions are executed in sequence automatically as long as there are no unhandled errors or rejected promise. However, for a second/third/forth then callback function to access the argument from the first then, it is not possible to access directly, unless you pass it along with every return from the subsequent then. This can be achieved easily by using then async/await operators instead of then.
How exactly does the code knows to jump to the catch function when an error is thrown? In an asynchronous function, throwing an error is almost equivalent to rejecting a promise. Think of it this way, every then callbacks are wrapped in a try/catch block that would call the function in the catch.
Edit: If you do fetch().then().then().catch(), all the errors or rejected promises will be handled in the catch function.
But if you do fetch().catch().then().then(), only the error or rejected promise from fetch will be handled in the catch function.
I'm trying to wrap my head around Nodejs and Promises and I'm seeing a behavior I don't quite understand so I'd like to get some help from the experts :)
I have trainStations.js file that returns information and caches it using node-cache library.
async function getTrainStations(){
var stations = await knex.select()
.from('TrainStations')
stations.forEach(
item => cache.set(item['code'], item['name'])
);
knex.destroy();
return cache;
}
module.exports = {
getTrainStations
};
Then. in app.js file, I call it as:
const stations = (async () => {
let res = await stationsModule.getTrainStations();
return res;
})();
If I debug "res" it is clearly a node-cache object, I can call the get method with a key and get its value. However, when it gets assigned to stations, it does it in the form of a resolved promise, and when I debug I can see the values, but I can't figure out how to return the value of the promise and use it as I want.
So basically the question is, why "res" got a proper node-cache object, but later on when I want to use "stations" I have a resolved promise instead. And also, how do I access to the values within the resolved promise? I tried with .then() and I can print the values in a console.log but can't use them at all.
Thanks in advance for the help!
async function calls always return a promise for the result of an [asynchronous] operation. In
const stations = (async () => {
let res = await stationsModule.getTrainStations();
return res;
})();
stations is assigned the promise returned by the anonymous arrow function call const stations = (async () => {...})();
The promise will be resolved with res when the data becomes available.
It may be worth noting you can't return an asynchronous result (obtained in the future) to the same call out from the event loop that requested the operation. In that turn of the event loop the result is pending.
Once await has been used, all code using the result must be in an async function, accessed using promise handlers in ordinary code, or triggered by a timer or event fired after the data arrives.
While I thought I was using Async and Await keywords properly, it appears as though I am doing something wrong.
I am using PouchDB, which is a great Javascript database that syncs with CouchDB. PouchDB uses promises for many of its functions, and has native support for Async and Await. As an example, to retrieve the basic information about a database, you would use the following code:
db.info().then(function (info) {
console.log(info);
})
I understand this, and it works. But if I try and put this inside a function, and then call that function, things go haywire. And I'm sure it's me and not PouchDB that is the problem...
function getLocalDBInfo(db){
try {
db.info().then(function (info) {
return info;
});
} catch (error) {
console.log("can't get local DB info: ", error);
}
}
async function testing(db){
try {
var info=await getLocalDBInfo(db);
await console.log("DB info=", info);
await console.log("Doc count= ", info.doc_count);
}
catch(err){
console.log("error=",err);
}
//info contains...
//{"doc_count":0,"update_seq":0,"db_name":"kittens"}
}
testing(MY_db);
If I log info inside the getLocalDBInfo function, it eventually (ie. after the promise fulfills) logs info to the console. But, the logs inside the testing function return immediately with undefined. It makes sense to me that they are returning undefined because they are returning immediately, but I was trying to have them wait for info by using async and await. Any suggestions as to what I am doing wrong?
getLocalDBInfo() is not returning a promise, so you can't await for it.
with async/await you could:
async function getLocalDBInfo(db){
try{
return await db.info()
} catch (err){
console.log("can't get local DB info: ", error);
}
}
you can also use new promise
getLocalDBInfo generates a promise, attaches a callback to it, then returns undefined. It doesn't return the resolved promise and you can't await on an undefined (well, you can, but it won't do anything).
You neee to change getLocalDBInfo to actually return the promise.
Also, why do you have a then block with the signature info => info? All this will do is unwrap the async value and then wrap it again. You can omit the then callback entirely.
Similarly, are you sure you need to wrap the db.info call in a try/catch? I wonder if you might be intending to use a promise.catch here instead. The try/catch construct only grabs promise rejections when awaiting an expression.
You need to return the value from the outer function getLocalDBInfo
function getLocalDBInfo(db) {
return new Promise((resolve, reject) => {
db.info()
.then(info => resolve(info))
.catch(err => reject(err))
})
}
I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.
I'm a bit confused about the MDN documentation of async await. These docs use the .then() syntax to respond when an async function has resolved:
mdn example
async function add() {
const b = await resolveAfter2Seconds(30); // timer
return b;
}
add().then(v => {
console.log(v);
});
But in my own code, I don't use .then() and it still works asynchronously. The flow is not blocked. So why would you use .then() ?
async code without .then
function start(){
console.log("starting!")
let d = loadData()
console.log("this message logs before loadData returns!")
console.log(d) // this shows: Promise - Pending because d has not yet returned
}
async function loadData() {
const response = await fetch("https://swapi.co/api/films/");
const json = await response.json();
console.log("data loaded!")
return json;
}
First of all, all async functions return a Promise, so if you want to get the returned value from that async operation, you'll either need to use then or await inside an async function.
MDN uses .then because the add async function is being called outside an async function scope, so it can't use await globally to catch the data from the promise.
In your example, you get the same, an instance of Promise as the return of your loadData async function, if you define the start function also as async, you can use let d = await loadData(), if it's not async, you can use .then (which is the Promise API).
Async function declaration async function loadData() returns AsyncFunction which is executed asynchronously and always returns a Promise.
Basically all the code you put inside the async function someFunctionName will be executed inside that Promise and when you return some value inside that function – it will resolve that promise.
So that then() call is to get the actual value from that Promise that your function returned.
Your code works because it is not returning promise object and actually waiting for the response. It returns the json at the end. So, execution holds till it gets the response.
If your function is async you don't need to return promise, your return statement will wait for all 'await' statement before it to complete.
Your own code works, because you replaced the usage of .then() with the usage of async await.