2

I am new to JavaScript (and stackoverflow as well) and learning about Promises. I'll try my best to use accurate terminology but sorry if some of it might be off.

Based off of what I've read my understanding is that JavaScript Promises are not handled by the JavaScript runtime environment, but rather (in the context of the web browser) they are handled by browser APIs. So when a Promise is created, the "executor function" passed to its constructor is actually "sent off" to the browser APIs to process, and the JavaScript thread of execution continues on processing the rest of the synchronous JavaScript code. And once the browser APIs have finished with the executor function of the promise and the value of the promise has been set, any callbacks added to that promise via the .then() function are added to the "microtask queue" to be added by the Event Loop to the Callstack once the Callstack is empty and there is no synchronous JavaScript code left to run.

With that understanding in mind I found myself asking the following question:

  1. Promises are written in JavaScript, but they are handled by the web browser APIs. Are you able to do things like update a global variable, or do console.log in the executor function that you pass to the promise constructor, even though that executor function will be handled by the browser APIs? In other words, does the web browser APIs have access to things like the global memory in the JavaScript runtime environment? And does it have access to things like console.log?

I tried searching around on the internet but couldn't find a direct answer, so I did some experimenting on code pen. I ran the following code (sorry if the styling isn't great, still new):

var myresolve = (val) => {console.log("resolved, promise value is: " + val)};
var myreject = (err) => {console.log("error: " + err)};
var counter = 0;
var mypromise = new Promise((myresolve, myreject) => {
  
    console.log("before promise blocking loop");
    for(i=0; i<3000000; i++){
        counter++;
    }
    console.log("after promise blocking loop");
    myresolve(10);
              })
mypromise.then((val) => {console.log("this should print last. Promise value is: " + val)});
console.log("before global blocking loop");
console.log("counter before global blocking loop: " + counter);
for(j=0; j<1500000; j++){
    counter++;
}
console.log("counter after global blocking loop: " + counter);

I was expecting the output to look something like below (I added numbering to denote line numbers for the output):

  1. before promise blocking loop
  2. before global blocking loop
  3. counter before global blocking loop: [some number between 0 and 3000000, I would think <1500000 by the time we get to this line of code]
  4. counter after global blocking loop: [some number >1500000]
  5. after promise blocking loop
  6. resolved, promise value is: 10
  7. this should print last. Promise value is: 10

I suppose lines 1-3 can be interchanged depending on what gets executed first if the synchronous JavaScript code is being run in parallel with the asynchronous Promise, but I would have expected line 4 to be printed before line 5 since the "promise blocking loop" blocks for longer than the the "global blocking loop". However, I get the following output on code pen:

  1. "before promise blocking loop"
  2. "after promise blocking loop"
  3. "before global blocking loop"
  4. "counter before global blocking loop: 3000000"
  5. "counter after global blocking loop: 4500000"
  6. "this should print last. Promise value is: 10"

It appears to me that, based off of this output, the promise executor function is being executed completely before the before it moves on to the "global blocking loop". Which suggests to me that it is not being executed asynchronously by the web browser APIs, but rather synchronously in the JavaScript runtime environment. So now I have some new questions:

  1. Are promise executor functions (the function you pass into the promise constructor that has the form (resolve, reject) => {/* some code */}) actually handled by the browser web APIs? Or are they handled in the JavaScript runtime environment.

  2. What happened to my expected output line 6? Did I have a syntax error, or a misunderstanding of what happens in the resolve function?

Here are some of the resources I looked at when learning about promises:

JavaScript the Hard Parts: Promises, Async & the Event Loop

Part 2: JavaScript the Hard Parts: Promises, Async & the Event Loop

When I use the term "JavaScript runtime environment" I am referring to the diagramming codesmith uses that includes the Global Execution Context, Global Memory, Call Stack, and I suppose the console as well.

Another resource I read through is

Synchronous vs Asynchronous JavaScript – Call Stack, Promises, and More

In the section "What is the Job Queue in JavaScript?" they say

Every time a promise occurs in the code, the executor function gets into the job queue.

Which leads to even more inconsistencies with the other resources I've read through. I believe both codesmith and freecodecamp are both pretty reputable resources. So I'm stuck wondering what is actually the case? Are Promise executor functions handled synchronously in the "JavaScript runtime environment" (which seems to be the case based off my experiment)? Are they handled by the web browser APIs in parallel with the synchronous JavaScript code being executed in the "JavaScript runtime environment" as suggested by codesmith? Are they added to the job queue (or as codesmith calls the microtask queue) and picked up by the event loop (which means that it will be deferred until after all synchronous JavaScript code has run) as suggested by freecodecamp? That is one of the difficult things I've found with learning to code - things are explained differently by different people. There is no consistency, which is what I would have expected from coding.

Sorry for the lengthy post. And thank you for taking the time to read it! Hopefully my questions make sense.

Final note: I did a lot of revising for this post so if any of the variables or values don't line up, sorry about that. Please let me know.

  • 1
    The executor function is executed synchronously, JS is single threaded unless you do not spawn child processes, this means that you won't expect to execute synchronous thread blocking code in a Promise in a non blocking way. There are some naturally async tasks that JS handles in a non blocking multi threaded way and they are HTTP calls, timeouts/intervals, and I/O operations. That's what you use promises for. They were handled with callbacks before promises, but the principle is the same. – Cesare Polonara Apr 24 '22 at 19:09
  • 1
    You can't pass custom `resolve, reject` functions like that, those are methods passed by the Promise constructor to your callback. Anyway I expect someone to answer with a richer and deeper insight on the subject since your question deserves it. – Cesare Polonara Apr 24 '22 at 19:16
  • 3
    The freecodecamp article is written very sloppily (at best) or just wrong (at worst). – Bergi Apr 24 '22 at 19:32
  • 1
    For your line 6, both your `myresolve = (val) =>` and `myreject = (err) =>` are unused in this code. The ones you call in the Promise executor are the ones passed by the constructor, they are completely different functions. Thus you never see the log. – Kaiido Apr 26 '22 at 01:52

0 Answers0