How exactly is a promise's value updated? - javascript

Let's suppose that a fetch request is executed at 2ms. The program keeps running. Then, at 100ms the request has been completed, but the program hasn't finished its execution.
1. At this point, does the browser actually update the value in the Promise object even though the program hasn't finished yet, or does this have to be done in Javascript's thread, when the call stack is empty?
2. How do the onFulfilled and onRejected callbacks get enqueued to the Job queue?
As far as I know, they do so when the Promise's state changes, but how exactly is this done?
Is the browser behind the scenes "watching" for changes in the object and enqueuing the callback immediately, or is it Javascript that does it when finishing executing synchronous code?
EDIT: I was told in a course that, roughly speaking, what happened under the hood was that the Promise object's property "value" was updated as soon as the request was successfully completed (even if the program was still running), and immediately triggered two things: a change on the Promise's state, and the callback in onFulfillment array to be enqueued in the microtask queue (assuming one was added using then). Is this possible? Does the Promise object actually get modified outside Javascript, or is this done when the program finishes its execution?
Please correct me if I make any mistakes.

Assuming that the fetch call will succeed without error, at 100ms when the Request has been correctly sent and the Body is being streamed, a new fetch task is enqueued in the Networking task source which will process the response. This final algorithm is the one responsible to resolve the Promise returned by window.fetch().
So if at 100ms, the browser has something else to do, it will continue to do whatever it was. If when it's done doing that, it feels it has a more important task to do (e.g UI event?), then it will do that before. When it will proceed that fetch task, it will resolve the Promise, and doing so, it will queue a microtask to execute your callback, right after that task is done processing (i.e synchronously here).

Related

Why is a XHR Request treated as a macro-task while a Fetch request is treated as a micro-task?

So based on 2 StackOverflow answers, what I have understood is:
XHR callback is queued with Macrotasks
Fetch method is queued with Microtasks
So my question is:
Is this true?
If yes, why is it this way? Shouldn't both of them be treated in the same way?
Is this true?
No. Re-read the answer your linked:
When the the request response will be received […], the browser will queue a new task which will only be responsible of resolving that Promise, […]
I've emphasised the macrotask for you.
Shouldn't both of them be treated in the same way?
No, why would they? One is a promise API, the other is not. Notice that if you wrap XMLHttpRequest in a promise, you get exactly the same behaviour: the load/readystatechange event (a macro task) resolves a promise, scheduling any promise handler (a micro task).
But ultimately you should ask yourself: does it even matter? You normally shouldn't need to concern yourself with such timing details.
Is this true?
Yes.
When XMLHttpRequest was created there was no microtask queue. Only one - what is now called the macrotask queue.
However, when fetch() was introduced, promises were already in the standard. The result of fetch() is a promise and all effects after a promise resolution are done via the microtask queue:
setTimeout(() => console.log("macrotask done"), 0); //logged second
Promise.resolve().then(() => console.log("microtask done")); //logged first
Hence resolving the promise from fetch() will also add the subsequent handlers to the microtask queue. Again, it is the one used for the handlers all promises.
If yes, why is it this way? Shouldn't both of them be treated in the same way?
There is no requirement for the two to work the same. Nor would the resolution of these make much of a practical difference in day-to-day code.
Do note that the two are not really the same, either - fetch will resolve as soon as a result is returned before the body of the result is read. Hence why calling .json() or .text() is needed, see Why does .json() return a promise? - calling those methods that will actually process the body. XHR does not have this intermediate step required, its body is processed once it assumes ready state 4 (done).

Understanding execution of WebAPIs with setTimeout & DOM Api

I have been working with JS for about a year now and I'm still learning it. My question is at the end.
In the first example:
console.log('Start');
setTimeout(()=> console.log('inside callback'), 0);
console.log('End');
outputs
Start
End
inside callback
I understand that even though the timeout is 0, it still had to go to browser WebApi (setTimeout api) and immediately pushed into callback queue waiting for the main thread to be free (which is currently executing console.log('End')). Once the thread is free callback is executed & "inside callback" is printed.
Following this approach, I tried to experiment it with another WebAPI i.e DOM API.
This time, Instead of setTimeout, I used document.querySelectorAll()
console.log('Start');
console.log(document.querySelectorAll('img'))
console.log('End');
Outputs
Start
NodeList(4) [img.bar-sm.-avatar.js-avatar-me, img.bar-sm.avatar.s-avatar--image, img, img]
End
This time we see DOM API output before END is printed.
I know the output but I want to understand the flow behind it.
Why it didn't follow the pattern like setTimeout?
Why didn't the DOM output came after "End" if it was processed on separate thread.
Did it follow the flow below:
document.querySelectorAll -> webAPI -> callback queue -> main thread
Does DOM API have to go though callback queue and wait for main thread to be empty. If so? how come we got consoles in the right order this time unlike with setTimeout
I'm still learning and would appreciate explanation a lot. Please point anything if I misunderstood. Thanks.
The reason is that the document.querySelectorAll is a synchronous method where as the setTimeout is asynchronous.
JS scripts are being executed in the same environment that the DOM lives in, so it is not asynchronous. Asynchronous events are events that generally get sent to another environment (later to be handled by the event loop)
This is part of the concurrency model based on an event loop, which is actually responsible for executing the code, collecting and processing event. The setTimeout function is called with 2 arguments: a message to add to the callback queue, and a time value (optional; defaults to 0). The time value represents the (minimum) delay after which the message will actually be pushed into the queue. If there is no other message in the queue, and the stack is empty, the message is processed right after the delay. However, if there are messages, the setTimeout message will have to wait for other messages to be processed and For this reason, the second argument indicates a minimum time (not a guaranteed time). So, the message 'end' will be written to the console before the message in the callback 'inside callback' gets processed, because the delay is the minimum time required for the runtime to process the request (not a guaranteed time).
document.querySelectorAll is part of scripts that are being executed in the same environment that the DOM lives in, so it is not asynchronous and will be executed immediately.
DOM manipulation is synchronous. having said that, the browser's re-rendering process of the page in response to a DOM update is asynchronous. This can give the illusion of an asynchronous DOM update.
More about the JavaScript event loop - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Mechanics of Javascript Asynchronous execution

Can someone please explain the mechanics behind the asynchronous behavior of javascript, specifically when it comes to a .subscribe(), or provide a link that would help me understand what's really going on under the hood?
Everything appears to be single-threaded, then I run into situations that make it either multi-threaded, or seem multi-threaded, because I still can't shake this feeling I'm being fooled.
Everything seems to run perfectly synchronously, one instruction at a time, until I call .subscribe(). Then, suddenly, something happens - it returns immediately, and somehow the next outside function runs even though the subscription code has not yet. This feels asynchronous to me, how does the browser keep things straight - if not threads, can someone please explain the mechanics in play that is allowing this to occur?
I also notice something happens with debug stepping, I can't step in to the callback, I can only put a breakpoint within it. In what order should I really expect things to occur?
Your Javascript itself runs single threaded unless you're talking about WebWorkers (in the browser) or WorkerThreads (in nodejs).
But, many functions you call or operations you initiate in Javascript can run separate from that single threaded nature of your Javascript. For example, networking is all non-blocking and asynchronous. That means that when you make a call to fetch() in the browser of http.get() in nodejs, you are initiating an http request and then the mechanics of making that request and receiving the response are done independent of your Javascript. Here are some example steps:
You make a call to fetch() in the browser to make an http request to some other host.
The code behind fetch, sends out the http request and then immediately returns.
Your Javascript continues to execute (whatever you had on the lines of code immediately after the fetch() call will execute.
Sometime later, the response from the fetch() call arrives back on the network interface. Some code (internal to the Javascript environment) and independent from your single thread of Javascript sees that there's incoming data to read from the http response. When that code has gathered the http response, it inserts an event into the Javascript event queue.
When the single thread of Javascript has finished executing whatever event it was executing, it checks the event queue to see if there's anything else to do. At some point, it will find the event that signified the completion of the previous fetch() call and there will be a callback function associated with that event. It will call that callback which will resolve the promise associated with the fetch() call which will cause your own Javascript in the .then() handler for that promise to run and your Javascript will be presented the http response you were waiting for.
Because these non-blocking, asynchronous operations (such as networking, timers, disk I/O, etc...) all work in this same manner and can proceed independent of your Javascript execution, they can run in parallel with your Javascript execution and many of them can be in flight at the same time while your Javascript is doing other things. This may sometimes give the appearance of multiple threads and there likely are some native code threads participating in some of this, though there is still only one thread of your Javascript (as long as we're not talking about WebWorkers or WorkerThreads).
The completion of these asynchronous operations is then synchronized with the single thread of Javascript via the event queue. When they finish, they place an event in the event queue and when the single thread of Javascript finishes what it was last doing, it grabs the next event from the event queue and processes it (calling a Javascript callback).
I also notice something happens with debug stepping, I can't step in to the callback, I can only put a breakpoint within it. In what order should I really expect things to occur?
This is because the callback isn't called synchronously. When you step over the function that you passed a callback to, it executes that function and it returns. Your callback is not yet called. So, the debugger did exactly what you asked it to do. It executed that function and that function returned. The callback has not yet executed. Your are correct that if you want to see the asynchronous callback execute, you need to place a breakpoint in it so when it does get called some time in the future, the debugger will then stop there and let you step further. This is a complication of debugging asynchronous things, but you get used to it after awhile.
Everything appears to be single-threaded, then I run into situations that make it either multi-threaded, or seem multi-threaded, because I still can't shake this feeling I'm being fooled.
This is because there can be many asynchronous operations in flight at the same time as they aren't running your Javascript while they are doing their thing. So, there's still only one thread of Javascript, even though there are multiple asynchronous operations in flight. Here's a code snippet:
console.log("starting...");
fetch(host1).then(result1 => { console.log(result1)});
fetch(host2).then(result2 => { console.log(result2)});
fetch(host3).then(result3 => { console.log(result3)});
fetch(host4).then(result4 => { console.log(result4)});
console.log("everything running...");
If you run this, you will see something like this in the console:
starting...
everything running...
result1
result2
result3
result4
Actually, the 4 results may be in any order relative to one another, but they will be after the first two lines of debug output. In this case, you started four separate fetch() operations and they are running independent of your Javascript. In fact, your Javascript is free to do other things while they run. When they finish and when your Javascript isn't doing anything else, your four .then() handlers will each get called at the appropriate time.
When the networking behind each fetch() operation finishes, it will insert an event in the Javascript event queue and if your Javascript isn't doing anything at the time, the insertion of that event will wake it up and cause it to process that completion event. If it was doing something at the time, then when it finishes that particular piece of Javascript and control returns back to the system, it will see there's an event waiting in the event queue and will process it, calling your Javascript callback associated with that event.
Search for articles or videos on the “JavaScript Event Loop”. You’ll find plenty of them. Probably go through a few of them and it will start to make sense (particularly the single-threaded aspect of it)
I found this one with a quick search, and it does a good, very high-level walkthrough.
The JavaScript Event Loop explained
The main impact of JavaScript single threading is that your code is never pre-emptively interrupted to execute code on another thread.
It may seem that some code is being skipped, but close attention to the syntax will show that what is being “skipped” is just a callback function or closure to be called at a time in the future (when it will be placed into the event queue and processed single-threadedly, itself).
Careful, it can be tricky to follow nested callbacks.
Probably also look into Promises and async /await (‘just syntactic convenience on the event loop, but can really help code readability)

What happens when you wait to resolve a promise in Node

const fetchPromise = fetch('example.com') // Takes 1 second to resolve
...
... // Do 3 seconds of work
...
const response = await fetchPromise // What happens with the promise between seconds 2-3?
What happens with the promise response before you resolve the promise?
In the example above, what happens with the fetch response in those 2 seconds where your program has items in its call stack, but you don't settle the promise? Does Node have some kind of cache for completed but unresolved promises?
here there is a great reading on the event loop.
In a high level perspective there is a forever running loop listening for events in the form of list of actions and processes them one at a time.
The code you shared above cannot be used globally due to the single threaded design of nodejs. What nodejs does is that every time you call a function it is pushed into the queue and processed. In every tick of the you pop something off the queue and run it or push something new.
In js world no 2 things can run at the same time. If you run a long running js code it blocks the entire system thus your system does not respond to new events since the eventloop is blocked.
That said there is another side of the story when you run all those nonblocking functions they are handled behind the scenes in v8 js engine. Since v8 native code it can make use of things like multiple threads and wait for things to finish or poll things checking whether they are ready and ones things are ready a new message gets inserted back into the js event loop and you get access to the data in js world.
So coming back to your question in an async function when you call await it really just means you yield the execution to the other things waiting in the message queue and once the thing you awaited gets ready it is pushed back to the queue and you continue from where you left of.
There is not really a waiting in the and the point of await is not to block the event loop with a blocking native function and to yield the execution to other things and you async function in the end of the day returns a Promise which only makes sense if you use .then and add a callback to be passed back to js world when the data is ready.

In which order these code statements are moved to call stack?

I am new to Javascript and trying to understand the execution engine of JS. I know that any async code statement moves to the call-stack and then gets immediately removed and executed in a separate Web API thread (started by browser). And then the result is stored in a callback queue and those values are picked up by the event loop once the call stack is empty.
Can anyone confirm the order in which below mentioned statements will be moved to call stack ?
Promise.resolve(function1)
.then(function2)
.then(function3)
.then(function4);
console.log("Hello");
Does the whole promise chain move to call stack or individual thens ?
Does the whole promise chain move to the call stack or individual thens ?
Not the whole chain. Also be careful how you express things here. The individual thens are executed synchronously. It is the callback to a then that is executed asynchronously. I suppose you refer to those callbacks.
The chained then methods are executed on different promise objects, and each will queue a reaction handler in the Promise Job Queue,
but only at the time the corresponding promise resolves.
So we have at least 4 promise objects that are created synchronously here. The call to Promise.resolve creates the first one,
which is immediately resolved. All three then methods are executed as well, creating 3 pending promises.
Lets call these 4 promise objects A, B, C and D. So A is resolved, the others are pending.
As promise A is resolved, an entry is put on the Promise Job Queue. Let's call that H(A) ("Handler for reacting to resolved promise A")
This all happens synchronously. After the synchronous script finally executes the console.log, the call stack is empty and the Promise Job Queue is processed:
The Promise Job Queue has H(A). It is pulled from that queue and put on the call stack.
This triggers the first then callback, i.e. function2.
Its return value is used to resolve promise B.
We should consider here the case where function2 returns a promise E (could be a thenable), but let's assume first that it is just returning a non-thenable.
Promise B is resolved with that value, and a new entry H(B) is put on the Promise Job Queue.
The callstack is empty again.
The Promise Job Queue is processed again, which now has H(B), ... and so it continues.
This will all happen in one single task if function2, function3 and function4 return non-thenables, but the Job Queue will not have H(A), H(B), H(C), H(D) at the same time.
The queue will in this scenario only have one of them at a time.
More realistic is when a function like function2 returns the result of a call to an asynchronous API, such as fetch.
In that case, promise B will be made dependent on the promise E that function2 returns. This dependency includes an asynchronous call to the then method of E.
Without going into too much detail about that process, the essence is that the Promise Job Queue may not get an entry H(E) immediately.
This will only happen when promise E resolves. And when it does, the call stack will get H(E) from the Job Queue.
Its execution will involve a call to H(B), because of the dependency.
Because of this delay, the current Task will find at some point that the Job Queue is empty, and the task ends.
It will be then for the Event Loop to detect when there is something ( like H(E) ) on the Job Queue.
Some other remarks
executed in a separate Web API thread
There is no separate Web API thread for executing such asynchronous code. All this happens in one thread.
That being said, there can be APIs involved that have non-JS parts, for instance accessing OS functions, which may execute via other (non-JS) threads. Also there is the concept of
Web Workers which get their own execution thread. But that has nothing to do with Promises.
the result is stored in a callback queue
It is not the result of asynchronous code execution that is stored in a callback queue. The Promise API stores a Promise Reaction Record in the queue at the time the promise is resolved. In case of Promise.resolve(), this actually happens as part of the synchronous script execution.
those values are picked up by the event loop once the call stack is empty.
There is a precision to make here: there are different queues. The Promise Job Queue has priority over other queues, and so for instance user I/O events will not be processed as long as there are entries on the Promise Job Queue. The execution of these entries are considered part of the same Task.

Categories

Resources