I'm learning Javascript async programming and Promise. I have the following code.
let promiseTest = () => new Promise((resolve, reject) => {
console.log("Inside Promise");
resolve('Promise is resolved');
});
console.log("Before Promise");
promiseTest().then((data) => { console.log(data); });
console.log("After Promise");
I have the function "promiseTest". I want to execute that asynchronously. In real world scenario, it could be a function that fetch the web service or query the database. I was expecting to have the following result from the code.
Before Promise
After Promise
Inside Promise
Promise is resolved
I was expecting that "console.log(" Inside Promise") " will be executed asynchronously since it is inside the Promise function. But the actual result is
Before Promise
Inside Promise
After Promise
Promise is resolved
As you see tin the actual result, Inside Promise is executed synchronously and blocking the main thread. Only the code inside "resolve" is asynchronous. Maybe, my current understanding of javascript Promise is incorrect. How do I modify my existing code to make sure the whole function " promiseTest " is executed asynchronously without blocking the main thread.
The executor function is executed synchronously. Promise doesn't make any code asynchronous; its just a mechanism to get notified when something, that is already asynchronous, has completed.
If you want to make a API call, that is already asynchronous and if you use the fetch API, then you don't need the promise constructor because fetch(...) already returns a promise.
Only the code inside "resolve" is asynchronous
In your code, resolve() function is also called synchronously; only the callback function of promiseTest().then(..) is invoked asynchronously.
How do I modify my existing code to make sure the whole function
"promiseTest" is executed asynchronously without blocking the main
thread.
You will have to use another thread. Even if you delay the execution of the promiseTest function using setTimeout, once it is pushed in the call stack, it will execute synchronously. Nothing else will execute when the delayed promiseTest function is being executed.
If delaying the execution is what you want, just wrap the promiseTest function call in setTimeout.
I'd suggest using setTimeout for your promiseTest function, this will give you the expected result.
setTimeout sets a timer which executes a function once the timer expires.
As you can see "After Promise" will be logged before "Promise is resolved", this is entirely expected, since the promise will take some time (in this case 1000 milliseconds) to resolve.
let promiseTest = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Inside Promise");
resolve('Promise is resolved');
}, 1000)
});
console.log("Before Promise");
promiseTest().then((data) => { console.log(data); });
console.log("After Promise");
Related
This question already has answers here:
Javascript async and await
(3 answers)
Closed 4 months ago.
I'm learning promises and I came across this piece of code
async function main() {
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("hello");
resolve();
}, 1000);
});
console.log("world");
}
main();
The output this produces is "hello world".
What I'm not able to understand is why the inner console log "console.log("world")" is waiting for the promise to resolve.
Am I missing something here, it would be nice if I can get some help understanding this or maybe some link to some documentation for further reading.
Thanks in advance.
Am I missing something here
Bluntly speaking yes, you're missing first word of the second line.
The key to understand what's happen there is await.
You're creating a promise with
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("hello");
resolve();
}, 1000);
});
This promise will start an asynchronous action, that could end somewhere in the future (after 1 sec in this particular case).
You probably supposed that this action should be executed after console.log("world"); and it could happen if there was no await before promise.
But we do have this await so we litteraly said to program "Hey, I want to wait for this action to end, before I'll go farther".
That's why we see printed hello before world.
Await expressions make promise-returning functions behave as though
they're synchronous by suspending execution until the returned promise
is fulfilled or rejected.
Source
Let's break this down step-by-step without overcomplicating things. This is what is happening inside your main() function:
the first line is executed as expected, but the keyword await inside your function stops the progress (the promise must be first fulfilled or rejected)
inside the promise you have setTimout(), another asynchronous function, but this one is not awaited;
setTimeout() is an asynchronous function, meaning that the timer
function will not pause execution of other functions in the functions
stack. In other words, you cannot use setTimeout() to create a "pause"
before the next function in the function stack fires.
source
Yet, the execution seems to be paused, but it is not due to setTimeout() function on its own. It is because the promise waits to be resolved. And when do we resolve it? After 1 second.
The callback inside setTimeout() is executed as expected: console.log('hello') is printed first and the promise is resolved right after with resolve().
With the promise being resolved, the progress finally continues with the console.log('world').
Thus we end up with
// hello
// world
You can try moving the resolve() outside of the setTimeout() and observe how the promise is resolved right away and the printed message is reversed (because, as I said above, setTimout() does not pause the execution of the promise on its own).
async function main() {
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('hello')
}, 1000)
resolve()
})
console.log('world')
}
// world
// hello
To wrap it up:
What I'm not able to understand is why the inner console log >"console.log("world")" is waiting for the promise to resolve.
The console.log("world") is not waiting for the promise, it is waiting for the delay inside setTimeout(). Just as promise is waiting to be resolved: which happens right after the console.log("world").
Because this is how promises work. They are designed to always return a clear result. Read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise for more info.
I have wrote the following code:
async function imaAsyncFunction () {
console.log('1234');
let res = await setTimeout(()=>{console.log('10 sec');},10000);
console.log(res);
console.log('is After?!?');
}
imaAsyncFunction();
console.log('bla bla bla');
I was a little surprise that '1234' isn't printed at all. In addition the line console.log(res); was printed before the 10 seconds of timeout was finished, but i guess that the reason is that the code after await doesn't return a Promise.
But, why does the code : console.log('bla bla bla'); was printed before both lines:
console.log(res);
console.log('is After?!?');
If we saw that the code doesn't postpone because of the setTimeout() so why both line of codes don't run before the function is finished and only after that the console.log('bla bla bla'); should run?
What am I missing here?
Does all async function code will run i a different thread?
No. async functions, await, and promises do not create or use different threads.
A promise doesn't make anything asynchronous on its own¹, it's just a way of observing the completion of something that's already asynchronous (like your timer callback, which uses the timer subsystem of your environment to call your timer callback later).
async functions are "just" a way (a really, really useful way) to wait for promises to settle via syntax rather than by attaching fulfillment and rejection handlers using the .then or .catch methods.
I was a little surprise that the line console.log(res); was printed before the 10 seconds of timeout was finished, but i guess that the reason is that the code after await doesn't return a Promise.
setTimeout doesn't return a promise, right. So await doesn't wait for the timer callback to occur. See this question's answers for how you'd make a promise-enabled version of setTimeout.
But, why does the code : console.log('bla bla bla'); was printed before both lines:
For the same reason: Your async function returns a promise, and your code calling your async function doesn't wait for that promise to be settled before moving on to the console.log('bla bla bla') line.
Here's code that both uses a promise-enabled version of setTimeout and waits for your async function's promise to settle before doing the console.log:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function imaAsyncFunction () {
let res = await delay(800); // 0.8s instead of 10s
console.log("Timer expired");
console.log(res); // undefined, since we haven't set a fulfillment value
console.log("is after");
}
imaAsyncFunction()
.then(() => {
console.log("bla bla bla");
})
.catch(error => {
console.error(error);
});
If you wanted the delay function to return a fulfillment value, we could do this:
function delay(ms, value) {
return new Promise(resolve => setTimeout(() => {
resolve(value);
}, ms));
}
although in all standard environments (now), setTimeout accepts a value to pass to its callback, so we can do just:
function delay(ms, value) {
return new Promise(resolve => setTimeout(resolve, ms, value));
}
function delay(ms, value) {
return new Promise(resolve => setTimeout(resolve, ms, value));
}
async function imaAsyncFunction () {
let res = await delay(800, 42); // 0.8s instead of 10s
console.log("Timer expired");
console.log(res); // 42
console.log("is after");
}
imaAsyncFunction()
.then(() => {
console.log("bla bla bla");
})
.catch(error => {
console.error(error);
});
In a comment you've said:
i still can't understand why both lines doesn't runs before the `console.log('bla bla bla');?
Here's your original code:
async function imaAsyncFunction () {
console.log('1234');
let res = await setTimeout(()=>{console.log('10 sec');},10000);
console.log(res);
console.log('is After?!?');
}
imaAsyncFunction();
console.log('bla bla bla');
When you run that code, here's what happens, in order:
The function imaAsyncFunction is created.
The call to imaAsyncFunction() is executed:
The synchronous part of the function is run:
console.log('1234'); is executed
setTimeout(()=>{console.log('10 sec');},10000) is executed
setTimeout returns undefined
The await is reached: (Note: I'm skipping some details below for clarity.)
Normally, when you do await p, p is a promise (or at least something like a promise). Since you've used it on undefined instead, await wraps undefined in a promise as though it called Promise.resolve(undefined) and used that as p instead of undefined.
All of the code that follows await in the function is attached to p as a handler for when p is fulfilled (since you haven't used try/catch — which is fine — so there's no rejection handler). When you attach a fulfillment or rejection handler to a promise, you create a new promise that gets settled based on what happens to the original promise and, if relevant, what happens when the fulfillment or rejection handler is called. Let's call that new promise p2.
Usually, p wouldn't be settled yet, but in this case it is, so the fulfillment handler (the rest of the code in imaAsyncFunction, after the await) is scheduled to run once the current synchronous work is completed. If p weren't settled, this wouldn't happen yet (it would happen later, when p is settled.)
Since there aren't any other await expressions in the function, the promise imaAsyncFunction implicitly created (let's call it p3) is resolved to p2. That means that when p2 settles, the promise from imaAsyncFunction is settled in the same way (it's fulfilled with the same fulfillment value as p2, or rejected with the rejection reason from p2).
imaAsyncFunction returns p3.
The call to imaAsyncFunction is done, so the next statement is executed: console.log('bla bla bla');
Code execution reaches the end of the script, which means all of the synchronous code has finished running.
The fulfillment handler code scheduled in Step 2.2 above is run:
It executes the console.log(res) and console.log('is After?!?').
It fufills p2 with undefined (since there's no return x in the function).
This schedules the fulfillment of p3, since p3 was resolved to p2. Doing that would scheduled execution of p3's fulfillment handlers, but it doesn't have any — nothing used await or .then or .catch, etc., on imaAsyncFunction()'s return value.
¹ There's one caveat to that statement: When you attach a fulfillment or rejection handler to a promise, the call to that handler is always asynchronous even if the promise is already settled. That's because it would be asynchronous if the promise weren't settled, and it would be chaotic if the call to your handler was synchronous in some situations but asynchronous in others, so it's always done asynchronously. But that's literally the only thing about promises or async functions that makes anything asynchronous, the rest just observes things that are already asynchronous.
I have the following code:
const doWorkPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('#2 (async call)');
}, 0);
resolve('#3 (async call?)');
console.log('#1 (sync call)');
});
doWorkPromise.then((result) => {
console.log('Success!', result);
}).catch((error) => {
console.log('Error!', error);
});
The output is:
#1 (sync call)
Success! #3 (async call?)
#2 (async call)
I know that sync code lands on the call stack and the async code registers event-callback pair at the Node APIs, where the callback is moved to the callback queue after the event. The event loop is then going to take elements out of the callback queue only when the call stack is empty. Therefore, I expected to have the sync code executed first and then the async one in order, because the timeouts are 0s, like:
#1 (sync call)
#2 (async call)
Success! #3 (async call?)
Can someone please explain why resolve is executed before '#2 (async call)'?
reject is never executed.
resolved is executed before #2 because:
setTimeout puts a function on the time out queue
resolve resolves the promise
console.log logs #1
The function finished
doWorkPromise.then assigns a function to run when the promise resolves
The promise is already resolved so that function is executed
After the minimum timeout time passes the function passed to setTimeout is called and logs #2
Your code executes the following synchronously:
resolve('#3 (async call?)');
...so that promise gets immediately in a resolved state. The fact that you have started a timer, as no bearing on the promise... it is unrelated.
So the then callback on that promise will be triggered on the next processing of the job queue, and so you'll see "Success", right after the execution of the main, synchronous code has completed.
Somewhat later (yes, even setTimeout(..., 0) will introduce a small delay), the timer will expire, but that event has nothing to do with any promise.
The common pattern
When you create a promise with new Promise and want it to resolve some time later, then you could indeed use setTimeout, but then the idea is to call resolve only when that timer has expired. Otherwise the timer does not do anything that serves the promise:
setTimeout(() => {
resolve();
}, 0);
This question is theoretical - I have no concrete problem to solve.
With that said, why does the async keyword wrap the return value of an async function in a promise? What's the point? Is it ONLY because the await expression expects a promise? Or is there some meaning / use behind this decision?
I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.
You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.
The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)
Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!
The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.
The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below
let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)
When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it
if however
We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.
When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}
container()
You can use await and .then interchangably
For example we could go:
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}
container()
once again the code will pause at .then and then continue when the asyncEvent promise has resolved.
In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
asyncEvent.then(() => console.log(scopedVariable))
The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.
In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.
I hope this helps!
The async and await operators are just syntactic sugar that hide the underlying use of promises to implement asynchronous code.
Using async before a function definition makes the function return a promise that resolves to the function's return value, rather than returning normally.
Using await before an asynchronous function call suspends the current function until the promise that it returns is resolved. It's basically equivalent to wrapping the remainder of the function in an anonymous function, and using that as the .then() callback of the promise.
For more information between the relationship, see How to translate Promise code to async await
I have some websocket code in JS. I have a message-handling loop like this:
socket.addEventListener('message', function (event) {
payload = JSON.parse(event.data)
method = payload.method
// Dispatch messages
if (method == 'cmd1') {
handle_cmd1(payload); // trigger event/semaphore here to wait up waiter
}
else if (method == 'cmd2') { ... }
});
And elsewhere, I have a button callback like this:
$('#my-button').change(function() {
handle_button();
});
async function handle_button() {
send_msg('msg1', 'hi');
// wait for server to reply with cmd1
cmd1_data = await something(); // what?
alert(`cmd1 data: $(cmd1_data)`);
}
The idea is that the button sends 'msg1' and the server is supposed to reply with 'cmd1' and some info. I want to wait for that reply and then do some more stuff.
So my question is how to interlock these? In C++ I'd use a semaphore. I'd rather not spin-loop; is there something in Javascript/JQuery I can use to trigger and then wait for a user-defined event like this? I'm sort of new to JS, and very new to JS async/await.
EDIT: I've made a simple jsfiddle to show what I'm after.
http://jsfiddle.net/xpvt214o/484700/
Now that I understand how promises in Javascript work, here's a working example of a promise that can get woken up from anywhere by calling a function:
wakeup = null;
// returns a promise that will be resolved by calling wakeup()
// (could be a list of these or whatever, this is just a simple demo)
function wakeable() {
return new Promise( (resolve) => {
wakeup = () => { resolve(true); }
});
}
// demo of waiting and getting woken up:
async function handle_event() {
while (true) {
console.log("waiting...")
await wakeable(); // returns to event loop here
console.log("handle event woke up!");
}
}
handle_event(); // start in "background"
wakeup(); // wake it up
setTimeout(_ => { wakeup(); }, 500); // wake it up again after a delay
What's happening here is that when you call wakeable(), it returns a promise. That promise is constructed with an anonymous function (the one taking resolve as arg); the promise constructor synchronously calls that function, passing it the promise's resolve method. In our case, the function sets wakeup to another anonymous function that calls the original resolve; it's a closure so it has access to that resolve function even when it's called later. Then it returns the new promise.
In this demo, we then await on that promise; that puts the pending promise on a queue, saves the current function state, and returns, like a generator calling yield.
A promise is resolved when its resolve function is called; in this case calling wakeup() calls the promise's internal resolve() method, which triggers any .then methods on the next tick of the Javascript event loop (using the promise queue mentioned above). Here we use await, but .then(...) would work the same way'
So there's no magic; I/O and timeout promises work the same way. They keep a private registry of functions to call when the I/O event or timeout happens, and those functions call the promise's resolve() which triggers the .then() or satisfies the await.
By the way, unlike async in python, leaving a pending promise "open" when the process exits is perfectly fine in Javascript, and in fact this demo does that. It exits when there's no more code to run; the fact that the while loop is still "awaiting" doesn't keep the process running, because it's really just some closures stored in a queue. The event loop is empty, so the process exits (assuming it's in node.js -- in a browser it just goes back to waiting for events).
something() should be a method that returns a promise() or should be another method that is also notated with async.
function something(){
return new Promise(resolve,reject) {
//... call your database
// then
resolve(theResult)
// or
reject(theError)
}
}
The async and await for the most part are really just wrappers around promises. The await returns when the promise calls resolve, and throws an exception when the promise calls reject.
Your async function can return another promise; If it returns another value, it gets turned into a resolved promise with that value.