Why then doesn't keep the order of the callbacks? - javascript

I have the following code:
Promise
.resolve('333')
.then(()=>{setTimeout(()=>{Promise.resolve('123');},10000)})
.then(()=>{console.log("should wait");});
I thought that the output should be first '123' and then 'should wait'. From unclear reason the 'should wait' is printed first. I thought that the second then won't start until the asynchrony function (setTimeout) won't finished. I read that this is all the "magic" of using Promise and then. Now i'm very confused. Why for example it doesn't happen when we call fetch function? fetch function is also asynchrony, so why the then after the fetch doesn't start before the fetch ends?

Unless a .then callback explicitly returns a Promise, the next .then in the chain is guaranteed to run nearly instantly afterwards (it gets put into the microtask queue).
Right now, you're not returning anything, so undefined gets returned, so the second .then runs immediately.
If you want the first .then to cause the second to wait until the timeout finishes, return a Promise that resolves when the timeout resolves:
Promise.resolve('333')
.then(() => {
return new Promise((res) => {
setTimeout(() => {
res('123');
}, 3000);
});
})
.then(() => { console.log("should wait 3 seconds"); });

Related

Why Javascript waits for promises to fulfil [duplicate]

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.

If an empty Promise is encountered within an infinite while loop, why is the while loop resolved with a pending Promise?

If I make a Promise that is never fulfilled:
const nothingPromise = new Promise((resolve) => {});
And then I await that Promise within an infinite while loop:
async function run() { while (true) { await nothingPromise;}}
Any then() function attached to the function will not run, but I don't get an infinite loop either. I get a pending Promise. In the Chrome console:
run().then(() => console.log('then'))
Promise {<pending>}
Why is a pending Promise returned? I have a feeling that it has something to do with this part of the ECMAScript spec:
The abstract operation LoopContinues takes arguments completion and labelSet and returns a Boolean. It performs the following steps when called:
If completion.[[Type]] is normal, return true.
If completion.[[Type]] is not continue, return false.
If completion.[[Target]] is empty, return true.
If completion.[[Target]] is an element of labelSet, return true.
Return false.
But I don't know which completion condition corresponds to await nothingPromise.
await sends the function it is in to sleep until:
The promise resolves and
The main event loop is free
So the while loop starts, the promise is awaited, and the function which calls run() receives the promise returned by run (which is pending because run is asleep) and continues.
Since nothingPromise never resolves, the run function never wakes up, so never completes, and never resolves the promise it returns.
The part of the specification you found is irrelevant since await sends run to sleep in the middle of the first iteration of the loop, so the loop never reaches completion.

Does all async function code will run i a different thread?

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.

Promises: why is the resolve executed after sync code and before async one

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);

Resolve await when message arrives

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.

Categories