Background
I am exploring Async/Await syntax, and I thought I had a clever way of waiting on CSS transitions built. It's not working, though, and I can't figure out why.
Code
const setStyleAndWait = async function( el, property, value ) {
var setTheStyle = (el, property, value) =>
new Promise(resolve => {
el.style[property] = value;
const transitionEnded = e => {
if (e.propertyName !== property) return;
el.removeEventListener('transitionend', transitionEnded);
resolve();
}
el.addEventListener('transitionend', transitionEnded);
});
await setTheStyle( this, property, value );
console.log('Run logic inside await\'s parent scope');
};
setStyleAndWait( element, 'opacity', '0' ); // element is a reference to an element with a transition on for it's opacity style
console.log('Run logic outside await\'s parent scope.)
Question
When I run this, the outer console.log runs immediately, while the inner waits until the promise is resolved. Why is the outer logic not waiting for the function to finish?
The setStyleAndWait function is asynchronous and not being awaited, so it returns immediately leading to that outer log finishing first. You can rewrite that as :
setStyleAndWait(element, 'opacity', '0').then(() => console.log('this runs after completion'))
or wrap the entire thing in an async function so you can use await.
why should it wait, when you declare a function with the async keyword, what you are saying is that your function is returning a promise. if you want the function caller to wait for the function to resolve/reject you have to use await or then/catch as you want.
In your code, you just have to add await to setStyleAndWait()
When you add async in front of a function, you are making that function asynchronous, it means that the function is executed but the code jumps to next line not waiting for the resolution.
So in order to make this work you need to encapsulate the entire code inside an async function, and make the constant setStyleAndWait to wait for a value to continue with an await
async function envelope(){
const setStyleAndWait = await function(//rest of code)
}
Related
I'm hoping my title and simplistic code explains what I'm looking to do. I'm trying to call myFunction(), then let those promises resolve with Promise.all... then, and then execute code after myfunction();
myfunction() {
let x = promiseOneFunction();
let y = promiseTwoFunction();
Promise.all([x, y]).then(values => {
let valOne = = values[0];
let valTwo = values[1];
returnObj = {
fistVal: valOne,
destination: valTwo
}
console.log("return object within promise is ", returnObj);
return returnObj;
});
} // end myfunction
myfunction();
console.log("want to have this log after my function is finished");
Thanks!
The only (sensible) way to run code when a promise completes is to either:
Call then() on the promise
await the promise
This means you need the promise to be available outside myfunction. Currently that function returns nothing. Change it so it returns the promise returned by Promise.all([x, y]).then(...) (which you currently ignore).
return Promise.all([x, y]).then(...)
Then that promise will be available outside the function:
const the_promise = myfunction();
And you can call then on it.
the_promise.then(() => {
console.log("want to have this log after my function is finished");
});
Due to the fact that JS is single threaded, all asynchronous functions will be put in to the event queue, which runs after the main thread has finished processing the current function/block of code.
If you want something to happen strictly after the async function you need to put it in a .then() block chained to the async function or you could use the new async/await syntax. It's the same.
With .then()
myAsyncFunc().then(result => {
console.log(result)
})
With async/await, you first declare your function as an async function
const myAsyncFunc = async () => {
//Content of your function such as Promise.all
//Returns a Promise
}
Then you call it like:
const result = await myAsyncFunc();
console.log(result)
I have two functions.
1.
async function firstFunction() {
// Do stuff here
// I can't just return doSomeOtherThings because the firstFunction
// returns different data then what doSomeOtherThings does
doSomeOtherThings();
return;
}
async function doSomeOtherThings() {
// do promise stuff here
// this function runs some db operations
}
If I run firstFunction(); will it execute my doSomeOtherThings() function, or will it return early from that and cause some or all of the doSomeOtherThings code to not be executed? Do I need to do await doSomeOtherThings()?
I think there is a bit of confusion here, so I'll try to start from the beggining.
First, async functions always return a promise. If you add another async function inside that, you COULD chain them and wait for a response before returning the first promise. However, if you dont await the inner function, the first function will be resolved while the second is still running.
async function firstFunction() {
if (I want to wait for doSomeOtherThings to finished before ending firstFunction){
await doSomeOtherThings();
} else if (I can finish firstFUnction and let doSomeOtherTHings finish later){
doSomeOtherThings
}
return;
}
async function init() {
const apiResponse = await firstFunction();
};
init();
You need to have await. Actually 'async/await' is just a syntax sugar.
You function updateOtherThings actually returns a Promise, and if you don't await it, then it simply doesn't run.
If you want simply to start and forget it, then write something like:
updateOtherThings().then(() => {});
This question already has answers here:
Async function without await in JavaScript
(4 answers)
Closed 2 years ago.
due to everyone's help, I got the logic behind async, promise, then, await.
I have one curisosity on top of its basic nature, which is what if
I declare async function but doesn't use await in it.
technically every argument within async function is invisibly capsulated by '.then()'
but how it works would be exactly same as synchronous function execution.
For example,
async function trackUserHandler() {
var positiondata= await getPosition();
var timerdata = await setTimer(2000)
console.log(positiondata, timerdata);
setTimer(1000).then(() => {
console.log('Timer done!');
});
console.log('one');
}
The console below doesn't run till the first two await function is done due to await(s) sitting before this.
console.log(positiondata, timerdata);
What if I don't put any await(s) in the async like below?
async function trackUserHandler() {
var positiondata= getPosition();
var timerdata = setTimer(2000)
console.log(positiondata, timerdata);
setTimer(1000).then(() => {
console.log('Timer done!');
});
console.log('one');
}
I test-run this code and behaves seemigly same as regular function without 'async'.
Of course behind the scene, everything in the function is encapsulated into 'then()' though.
Am I understanding right?
Thank you in advance.
Behind the scene- If I use Aync but doesn't use await in it, would it be identical with normal function?
Yes, you are right. An async function without an await expression will run synchronously and would be identical with normal function, but the only difference is that async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
For example, the following:
async function foo() {
return 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1)
}
If there is an await expression inside the function body, however, the async function will always complete asynchronously.
For example:
async function foo() {
await 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1).then(() => undefined)
}
Code after each await expression can be thought of as existing in a .then callback.
Yes, an async function runs synchronously till the first await, so it behaves like a regular function. It does return a Promise though:
function trackUserHandler() {
// ... code
return Promise.resolve(undefined);
}
In your example, the 2 functions won't behave the same. Without the await keyword, your variables won't capture the results returned by these 2 functions, but instead receive 2 Promises.
var positiondata = getPosition();
var timerdata = setTimer(2000);
So your console.log will print out 2 Promises instead of the values you actually expect.
An async function can contain an await expression, that pauses the
execution of the async function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value.
As you assumed, if no await is present the execution is not paused and your code will then be executed in a non-blocking manner.
const getPromise = async (s) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(s), 500);
});
}
(async() => {
try {
const result = getPromise("a"); //no await, result has not been unwrapped
console.log('async/await -> ', result);
} catch (err) {
console.log(err);
}
})();
Async function without await in Javascript
I have async function that returns true or false. It looks like following one:
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return this.id === group.manager.id
}
}
If group parameter is ID of group then function will make async call to DB, so function canManageGroup will execute asynchronously. But if group parameter is group model then function will only call return this.id === group.manager.id or it will execute syncronously. Is it good practice to write code in this way? Or I should transform synchronous code to asynchronous?
function makeAsync(cb) {
return new Promise(resolve => setImmediate(() => resolve(cb())))
}
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return await makeAsync(() => this.id === group.manager.id)
}
}
You can use the first example without problems.
When you use async, your function will return a Promise. If your code is sync, the status of the returned promise will be resolved and it is safe to run any promise-related code on it (then, catch, etc).
For example:
async function truePromise() {
return true;
}
truePromise().then(function(value){
console.log("Promise value:", value);
});
Just works :)
Should you do it?
Yes. It is okay and works okay thanks to the async keyword.
What you MUST NOT do is the following:
function dontDoIt(doSync) {
if (doSync) return false;
return Promise.resolve(false)
}
Why? Because:
if doSync is truhtly, it will return false (i.e a boolean)
if doSync is falsy, it will return a Promise that will resolve to false.
That is a HUGE difference.
Why?
Your function sometimes returns a promise, and other times return a boolean. It is inconsistent.
doSync(true) is a boolean, you can't use await nor .then.
doSync(false) is a Promise, you can use await and then.
Based on your comment to one of the answers you are concerned because of the answer to the question How do you create custom asynchronous functions in node.js?
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
In js enviroments like nodejs is common practice that the callback that could be executed async, should always be called async, no matter if the actual code was really async.
So you are wondering if the following code will break that common practie.
async function doSomething() {
return true
}
doSomething()
.then(res => {
console.log(res)
})
This is not the case, because here you deal with Promises and a Promise is allowed to be resolve immediatly, the code above is basicly the same as if you would write:
Promise.resolve(true)
.then(res => {
console.log(res)
})
The async behaviour is ensured in the chaining part (then/catch), the callback you pass to the then/catch will be called async:
async function doSomething() {
return true
}
console.log('before');
doSomething()
.then(res => {
console.log('with in callback')
})
console.log('after');
As you can see the order of the logs is:
before
after
with in callback
So this is in the same order you would expect from an regular async callback function.
The first one is correct. There is no need to force anything to async if there doesn't need to be. Context switching isn't free, so if it doesn't have to wait for anything to complete, don't try and make it.
I'm trying to learn async-await. In this code -
const myFun = () => {
let state = false;
setTimeout(() => {state = true}, 2000);
return new Promise((resolve, reject) => {
setTimeout(() => {
if(state) {
resolve('State is true');
} else {
reject('State is false');
}
}, 3000);
});
}
const getResult = async () => {
return await myFun();
}
console.log(getResult());
why am I getting output as -
Promise { <pending> }
Instead of some value? Shouldn't the getResult() function wait for myFun() function resolve it's promise value?
If you're using async/await, all your calls have to use Promises or async/await. You can't just magically get an async result from a sync call.
Your final call needs to be:
getResult().then(response => console.log(response));
Or something like:
(async () => console.log(await getResult()))()
What you need to understand is that async/await does not make your code run synchronously, but let's you write it as if it is:
In short: The function with async in front of it is literally executed asynchronously, hence the keyword "async". And the "await" keyword wil make that line that uses it inside this async function wait for a promise during its execution. So although the line waits, the whole function is still run asynchronously, unless the caller of that function also 'awaits'...
More elaborately explained: When you put async in front of a function, what is actually does is make it return a promise with whatever that function returns inside it. The function runs asynchronously and when the return statement is executed the promise resolves the returning value.
Meaning, in your code:
const getResult = async () => {
return await myFun();
}
The function "getResult()" will return a Promise which will resolve once it has finished executing. So the lines inside the getResult() function are run asynchronously, unless you tell the function calling getResult() to 'await' for it as well. Inside the getResult() function you may say it must await the result, which makes the execution of getResult() wait for it to resolve the promise, but the caller of getResult() will not wait unless you also tell the caller to 'await'.
So a solution would be calling either:
getResult().then(result=>{console.log(result)})
Or when using in another function you can simply use 'await' again
async function callingFunction(){
console.log(await(getResult());
}
This is my routine dealing with await and async using a Promise with resolve and reject mechanism
// step 1 create a promise inside a function
function longwork()
{
p = new Promise(function (resolve, reject) {
result = 1111111111111 // long work here ;
if(result == "good"){
resolve(result);
}
else
{
reject("error ...etc")
}
})
return p
}
// step 2 call that function inside an async function (I call it main)and use await before it
async function main()
{
final_result = await longwork();
//..
}
//step 3 call the async function that calls the long work function
main().catch((error)=>{console.log(error);})
Hope that saves someone valuable hours
What hasn't been mentioned in this discussion are the use-case implications of the behaviour. The key thing, as I see it, is to consider what you are planning to do with the output from the top level, truly asynchronous function, and where you are planning to do that.
If you are planning to consume the output immediately, i.e. within the "async" function that is awaiting the return of the top level asynchronous function, and what you do with the output has no implication for other functions deeper in the call stack, then it does not matter that the deeper functions have moved on. But if the output is needed deeper in the call stack, then you need use "async" functions making await calls all the way down the stack to that point. Once you reach a point in the call stack where the function does not care about the asynchronous output, then you can stop using async functions.
For example, in the following code, function B uses the stuff returned from function A so is declared "async" and awaits A(). Function C() calls B(), is returned a Promise, but can move straight on before that promise is resolved because it is not interested in A()'s stuff, nor what's done with it. So C does not need to be declared as async, nor await B().
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomethingwith(bstuff);
return;
}
function C() {
B();
dontwaitmoveon();
...
return;
}
In this next example, C() does use A()'s stuff, so needs to wait for it. C() must be declared "async" and await B(). However D() does not care about A()'s stuff, nor what's done with it, so moves on once C() returns its promise.
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomething();
return bstuff;
}
async function C() {
var cstuff = await B();
dosomethingwith(cstuff);
...
return;
}
function D() {
C();
dontwaitmoveon();
...
return;
}
Since figuring this out, I have tried to design my code so the stuff returned by the asynchronous function is consumed as close as possible to the source.
Though your "getResult" function is async and you have rightly made an await call of myFun, look at the place where you call the getResult function, it is outside any async functions so it runs synchronously.
So since getResult called from a synchronous point of view, as soon as it is called, Javascript synchronously gets whatever result is available at the moment, which is a promise.
So returns from an async function cannot be forced to await(very important), since they are synchronously tied to the place of origin of the call.
To get what you want you can run the below,
async function getResult() {
const result = await myFun();
console.log(result);
//see no returns here
}
getResult();