I have a function that makes calls to multiple asynchronous functions.
$("#signUpForm").validator().on("submit", function(event) {
if (event.isDefaultPrevented()) {
// handle the invalid form...
sformError();
ssubmitMSG(false, "Please fill all fields!");
} else {
// everything looks good!
event.preventDefault();
//create use when form is submit
ssubmitForm();
//send email verification
sendEmailVerification();
//sign out the user
signOut();
}
});
the functions ssubmitForm() , sendEmailVerification() and SignOut() are all declared asynchronous and all have await statements in them.
I am getting weird behavior that seems to signal to me that these three functions are not running in the order they are written in the code. I thought that with asynchronous code all other code pauses execution and waits for the await statement to finish. But from my logs, I neah function it doesn't seem so. Is this behavior only within the scope of the asynchronous function itself? What is the execution order of the three functions? Can sendEmailVerification() be called before ssubmitForm() is done execution?
Also, I'm not sure if I'm using the correct verbiage/vocabulary to describe my problem. This has been a big problem for me as I am not easily able to search for my issues if I don't know the vocab. Please feel free to correct me on any misuse of terminology.
They are started in the order that they are called, but because they are async, they might not run in the order they are called. They return immediately and return an unresolved Promise, which will eventually resolve (or reject) asynchronously when the called function completes.
You need to mark your function async and await each of the calls in turn:
$("#signUpForm").validator().on("submit", async function(event) {
if (event.isDefaultPrevented()) {
// handle the invalid form...
sformError();
ssubmitMSG(false, "Please fill all fields!");
} else {
// everything looks good!
event.preventDefault();
//create use when form is submit
await ssubmitForm();
//send email verification
await sendEmailVerification();
//sign out the user
await signOut();
}
});
To better understand async/await, I recommend learning about promises first. They are the underlying model that makes everything tick; async/await is just syntactic sugar on top.
Related
I have a simple function that aims to log a user in, and some guard clauses in it to rule out errors
async signIn(email: string, passwort: string): Promise<void> {
const user: IPerson = await this.fetchUser(email);
if (user === undefined) {
return Promise.reject('Wrong email');
}
if (user.passwort !== passwort) {
return Promise.reject('Wrong password');
}
if (!this.logInUser(user)) {
return Promise.reject('Login Failed');
}
return Promise.resolve();
}
I used async await to wait for the promise resolve of fetchUser giving me the user, but this made me think, what benefits does await have here?
As summarized in some blog this is how async works:
"Let’s emphasize: await literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn’t cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc."
But in my case, there is no other other jobs in the meantime, the function can only resolve if the fetchUser provides something. Doesnt this mean JS Engine automatically waits and behaves just the same like using async? That would make it redundant.
what benefits does await have here?
It lets you write syntax that is easier to follow than using then with a callback.
That's all.
But in my case, there is no other other jobs in the meantime, the function can only resolve if the fetchUser provides something.
fetchUser is asynchronous. It isn't made asynchronous by the use of await. It provides a promise.
If you don't await that promise then you are just changing where the code execution is suspended.
Instead of signIn being suspended and the code that (you say) does nothing outside it continuing, you'll instead only suspend fetchUser and signIn will continue processing with the promise returned by fetchUser instead of the User object that that promise eventually resolves with.
Doesn't this mean JS Engine automatically waits and behaves just the same like using async? That would make it redundant.
No, the JS engine doesn't automatically "wait". Without await it would immediately continue executing the if statements, which obviously is not desired, as then you would not have the actual user object to work with, but "just" a promise object.
If the only thing that you wanted to do was to call fetch then it would be redundant to await it, but since you need the response from that request, you need to get informed when that response is available, and that is what await is offering you. You could alternatively chain a then call, whose callback would be called when the response becomes available.
I was wondering if there is any to cancel / stop execution of a javascript function that contains multiple await functions. Due to the nature of promises and their lack of proper cancellations, is there any other implementation or library to help me achieve something like this?
async function run(x,y,z) {
return new Promise(async(resolve,reject) => {
await doSomething(x)
await doSomething(y)
//cancel could be happen around here and stop the last "doSomething"
await doSomething(z)
})
}
setTimeout(() => {
run.cancel()
},500) //cancel function after 500ms
To just stop the advancement from one function call to the next, you can do something like this:
function run(x, y, z) {
let stop = false;
async function run_internal() {
await doSomething(x)
if (stop) throw new Error("cancelled");
await doSomething(y)
if (stop) throw new Error("cancelled");
await doSomething(z)
}
return {
cancel: () => {
stop = true;
},
promise: run_internal();
};
}
const retVal = run(a, b, c);
retVal.promise.then(result => {
console.log(result);
}).catch(err => {
console.log(err);
})
setTimeout(() => {
retVal.cancel()
}, 500); //cancel function after 500ms
Javascript does not have a generic way to "abort" any further execution of a function. You can set a flag via an external function and then check that flag in various points of your function and adjust what you execute based on that flag.
Keep in mind that (except when using workerThreads or webWorkers), Javascript runs your code in a single thread so when it's running, it's running and none of your other code is running. Only when it returns control back to the event loop (either by returning or by hitting an await) does any of your other code get a chance to run and do anything. So, "when it's actually running", your other code won't be running. When it's sitting at an await, your other code can run and can set a flag that can be checked later (as my example above shows).
fetch() in a browser has some experimental support for the AbortController interface. But, please understand that once the request has been sent, it's been sent and the server will receive it. You likely won't be aborting anything the server is doing. If the response still hasn't come back yet or is in the process of coming back, your abort may be able to interrupt that. Since you can't really know what is getting aborted, I figure it's better to just put a check in your own code so that you won't process the response or advance to further processing based on a flag you set.
You could wrap this flag checking into an AbortController interface if you want, but it doesn't change the fundamental problem in any way - it just affects the API you expose for calling an abort.
The way to actually use cancellation is through the AbortController which is available in the browser and on Node 15+
Node reference: https://nodejs.org/api/globals.html#class-abortcontroller
MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
Some APIs are currently using out of the box the abort signal like fetch in the browser or setTimeout timers API in Node (https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options).
For custom functions/APIs you need to implement it by yourself but it's highly encouraged to follow the Abort signal methodology so you can chain both custom and oob functions and make use of a single signal that does not need translation
I've recently begun working with Firebase Cloud Functions, and I'm slightly confused as to how to appropriately terminate this HTTP function:
exports.UpdateUserInfo= functions.https.onRequest( async (request, response) => {
try{
//Make a read call to Realtime DB
const snapshot = await admin.database().ref('/UserData').get()
if (snapshot.exists() == false) {
response.send("No users found")
return null
}
//All of the remaining code within the scope of the try block executes
functions.logger.log("Wait, this function should have ended")
let updateUserInfo = await userInfoUpdater(response)
}
catch{
functions.logger.info(error);
response.status(500).send("ERROR")
}
})
From what I've read, the correct way to terminate an HTTP function is to send a response via the response object. However, it appears that unless I include a final call to return null, the function continues to execute beyond its intended lifespan. Even worse, the function terminates and still allows the execution of additional network calls making things quite unpredictable and unorganized (see logs). I'd like to prevent the function from continuing once the conditional is met. Is returning null the best way to ensure proper termination, or am I missing something?
Calling response.send() does not terminate the function immediately. It merely signals to Cloud Functions that the function should shut down after the current block of code returns, either by return statement or falling off the end of the function block. If the function doesn't return in one of these two ways, then Cloud Functions will either time out or have other problems as the CPU is clamped down soon after the response is sent.
Essentially, sending the response should be the very last thing the function does before it returns. Anything else is prone to error.
I have a Vuex store and I am trying to fetch data from the Firebase Realtime Database. I am initially fetching the user information, however afterwards I would like to fetch some other information that relies upon the initial data fetched.
As you can see from the code, I am trying to do this using async / await, however whenever firing the two actions in my created() hook, the user's information isn't initialised, and therefore the second action fails.
My user store
async fetchCreds({ commit }) {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
return commit('SET_USER', userDoc.data())
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
My club action which relies upon the above call
async fetchClubInformation({ commit, rootState }) {
try {
const clubIDForLoggedInUser = rootState.user.clubId
const clubDoc = await clubs.doc(clubIDForLoggedInUser).get()
return commit('SET_CLUB_INFO', clubDoc.data())
} catch (error) {
console.log(error)
}
}
}
The methods being called within my component's created() method.
created: async function() {
await this.fetchCreds();
await this.fetchClubInformation();
this.loading = false;
}
I have a feeling I'm fundamentally misunderstanding async / await, but I can't understand what in the code is incorrect - any help or advice would be greatly appreciated.
I'm not particularly familiar with Firebase but after a bit of digging through the source code I think I can shed a little light on your problems.
Firstly, consider the following example:
async function myFn (obj) {
obj.method(function () {
console.log('here 1')
})
console.log('here 2')
}
await myFn(x)
console.log('here 3')
Question: What order will you see the log messages?
Well here 2 will definitely come before here 3 but it's impossible to tell from the code above when here 1 will show up. It depends on what obj.method does with the function it's been passed. It might never call it at all. It might call it synchronously (e.g. Array's forEach method), in which case here 1 will appear before the other messages. If it's asynchronous (e.g. timers, server calls) then here 1 may not show up for some time, long after here 3.
The async modifier will implicitly return a Promise from the function if it doesn't return a Promise itself. The resolved value of that Promise will be the value returned from the function and the Promise will resolve at the point the function returns. For a function without a return at the end that's equivalent to it finishing with return undefined.
So, to stress the key point, the Promise returned by an async function will only wait until that function returns.
The method onAuthStateChanged calls its callback asynchronously, so the code in that callback won't run until after the surrounding function has completed. There's nothing to tell the implicitly returned Promise to wait for that callback to be invoked. The await inside the callback is irrelevant as that function hasn't even been called yet.
Firebase makes extensive use of Promises, so typically the solution would just be to return or await the relevant Promise:
// Note: This WON'T work, explanation follows
return firebase.auth().onAuthStateChanged(async function(user) {
// Note: This WON'T work, explanation follows
await firebase.auth().onAuthStateChanged(async function(user) {
This won't work here because onAuthStateChanged doesn't actually return a Promise, it returns an unsubscribe function.
You could, of course, create a new Promise yourself and 'fix' it that way. However, creating new Promises using new Promise is generally considered a code smell. Typically it's only necessary when wrapping code that doesn't support Promises properly. If we're working with a library that has proper Promise support (as we are here) then we shouldn't need to create any Promises.
So why doesn't onAuthStateChanged return a Promise?
Because it's a way of watching all sign-in/sign-out events. Every time the user signs in or signs out it'll call the callback. It isn't intended as a way to watch a particular sign-in. A Promise can only be resolved once, to a single value. So while a single sign-in event could be modelled with a Promise it's meaningless when watching all sign-in/sign-out events.
So fetchCreds is registering to be notified about all sign-in/sign-out events. It doesn't do anything with the returned unsubscribe function, so presumably it'll be listening to all such events until the page is reloaded. If you call fetchCreds multiple times it'll keep adding more and more listeners.
If you're waiting for a user to finish signing in then I suggest waiting for that directly instead. firebase.auth() has various methods starting with the prefix signIn, e.g. signInWithEmailAndPassword, and these do return a Promise that resolves when the user has finished signing in. The resolved value provides access to various information, including the user. I don't know which method you're using but the idea is much the same for all of them.
However, it might be that you're really just interested in grabbing the details of the current user. If that's all you want then you don't need to use onAuthStateChanged at all. You should just be able to grab a copy using the currentUser property. Something like this:
async fetchCreds({ commit }) {
try {
const { uid } = firebase.auth().currentUser
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
As I've already mentioned, this relies on the assumption that the user is already signed in. If that isn't a safe assumption then you might want to consider waiting until after sign in has completed before creating components that need user credentials.
Update:
Questions from the comments:
If the obj.method() call was asynchronous and we did await the callback function within it, would that ensure that the outer async function (myFn) never resolves before the inner one has finished?
I'm not entirely sure what you're asking here.
Just to be clear, I'm being very careful with my use of the words async and asynchronous. A function such as setTimeout would be considered asynchronous but it is not async.
async/await is just a lot of syntactic sugar around Promises. You don't really wait for a function, you wait for a Promise. When we talk about awaiting an async function we're really talking about waiting for the Promise it returns to resolve.
So when you say await the callback function it's not really clear what that means. Which Promise are you trying to await?
Putting the async modifier on a function doesn't make it magically wait for things. It will only wait when it encounters await. You can still have other asynchronous calls within an async function and, just like with a normal function, these calls will be performed after the function has returned. The only way to 'pause' is to await a Promise.
Putting an await inside another function, even a nested function, won't make any difference to whether the outer function waits unless the outer function is already waiting for the inner function. Behind the scenes this is all just Promises chaining then calls. Whenever you write await you're just adding another then call to a Promise. However, that won't have the desired effect unless that Promise is in the same chain as the Promise returned by the outer async function. It only needs one link to be missing for the chain to fail.
So modifying my earlier example:
async function myFn (obj) {
await obj.method(async function () {
await somePromise
// ...
})
// ...
}
await myFn(x)
Note that there are 3 functions here: myFn, method and the callback passed to method. The question is, will await myFn(x) wait for somePromise?
From the code above we can't actually tell. It would depend on what method does internally. For example, if method looked like this then it still wouldn't work:
function method (callback) {
setTimeout(callback, 1000)
}
Putting async on method won't help, that'll just make it return a Promise but the Promise still won't be waiting for the timer to fire.
Our Promise chain has a broken link. myFn and the callback are both creating their parts of the chain but unless method links those Promises together it won't work.
On the other hand, if method is written to return a suitable Promise that waits for the callback to complete then we will get our target behaviour:
function method (callback) {
return someServerCallThatReturnsAPromise().then(callback)
}
We could have used async/await here instead but there was no need as we can just return the Promise directly.
Also, if in the async myFn function you're not returning anything, does that mean it'll resolve immediately and as undefined?
The term immediately is not well-defined here.
If a function isn't returning anything at the end then it's equivalent to having return undefined at the end.
The Promise returned by an async function will resolve at the point the function returns.
The resolved value for the Promise will be the value returned.
So if you aren't returning anything it will resolve to undefined. Resolving won't happen until the end of the function is reached. If the function doesn't contain any await calls then this will happen 'immediately' in the same sense as a synchronous function returning 'immediately'.
However, await is just syntactic sugar around a then call, and then calls are always asynchronous. So while the Promise might resolve 'immediately' the await still has to wait. It's a very short wait, but it isn't synchronous and other code may get the opportunity to run in the meantime.
Consider the following:
const myFn = async function () {
console.log('here 3')
}
console.log('here 1')
Promise.resolve('hi').then(() => {
console.log('here 4')
})
console.log('here 2')
await myFn()
console.log('here 5')
The log messages will appear in the order they're numbered. So even though myFn resolves 'immediately' you'll still get here 4 jumping in between here 3 and here 5.
To make it short
fetchCreds({ commit }) {
return new Promise((resolve, reject) => {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
resolve()
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
resolve()
}}
}
async () => undefined // returns Promise<undefined> -> undefined resolves immediatly
asnyc () => func(cb) // returns Promise<any> resolves before callback got called
() => new Promise(resolve => func(() => resolve())) // resolves after callback got called
So I have this problem. I'm fairly new to angular and I've been told to modify a directive which manages forms to make the submit button disabled then enabled again when all the work is done.
Since the functions being called usually have async calls, simply adding code sequentially doesn't work.
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction();
vm.disabled = false;
}
Button is enabled before async calls under ngSubmitFunction() finish.
So I thought a promise would fix that and made something like:
var promise = function() {
return $q(function (resolve) {$scope.ngSubmitFunction()});
}
var ngSubmit = function() {
vm.disabled = true;
promise().then(function() {
vm.disabled = false;
});
}
This doesn't output any error but never enables the button again (.then is never called).
Tried different kind of promises declaration, all with the same result, except for this one:
$scope.submitPromise = function() {
return $q.when($scope.ngSubmitFunction());
}
This does call .then function, but again, doesn't wait for any child async function to finish. '.then' is called instantly, like the sequential version.
Have in mind that I don't know what's under ngSubmitFunction(). It is used by dozens developers and it may contain from 0 to multiple async calls. But typical scenario is something like:
ngSubmitFunction() calls func()
-- func() decides wether to call create() or update()
-- -- update() calls a elementFactory.update() which is an async call
-- -- -- elementFactory.update().then(function()) is called when finished.
-- -- -- -- At THIS point, I should enable the button again.
How can I achieve this? is there a way to chain promises with non-promises functions in between? or another way to make a code only execute when everything else is done? I thought about creating an event at DataFactory when an async call is over but if the update() function was calling to more than one async call this wouldn't work.
If you are using promises, your async functions should return promises, if they do it should work like this:
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction().then(() => {
vm.disabled = false;
});
}
I don't know what's under ngSubmitFunction()
Then you're out of luck, promises won't help you here. Promises or $q.when cannot magically inspect the call and see what asynchronous things it started or even wait for them - ngSubmitFunction() needs to return a promise for its asynchronous results itself.
You need to make every function in your codebase which (possibly) does something asynchronous that needs to be awaitable return a promise. There's no way around this.
Well, in case someone wonders, we haven't found a solution to that (there probably isn't). So we will go with the adding returns to all the chain of functions to make sure the ngSubmitFunction recieves a promise and therefor can wait for it to finish before calling '.then'. Not only this makes it work for the cases where there's only one promise implied but it is also a good programming practice.
Cases with more than one promise are scarce so we will treat them individually on the controller itself.
Thank you all for your comments.