title: How does the JS event loop behave when a Promise's resolution depends on setTimeout?
body: <pre><code>console.log('1')
  setTimeout(() =&gt; {
      console.log('2')
  }, 0)
  function three() {
      return new Promise(resolve =&gt; {
          setTimeout(() =&gt; {
              return new Promise(resolve =&gt; resolve('3'))
          },0)
      })  
  }
  three().then(result =&gt; console.log(result))
  console.log('4')
  </code></pre>
  
  <p>This code snippet outputs <code>1 4 2</code></p>
  
  <p>This is the behavior I would expect based on my understanding of javascript's event loop and concurrency model. But it leaves me with some lingering questions.</p>
  
  <p>Before getting to those questions, I'll first break down my understanding of this code snippet.</p>
  
  <p><strong>Why code outputs 1</strong></p>
  
  <p>no explanation needed</p>
  
  <p><strong>Why code outputs 4</strong></p>
  
  <p>the callback that outputs 2 gets loaded into the event queue (aka macro task queue) after 0ms, but doesn't get executed until the main call stack is emptied. </p>
  
  <p>even if <code>three</code> was a promise that was immediately resolved, its code is loaded into the job queue (aka microtask queue) and wouldn't be executed until the main call stack is emptied (regardless of the contents of the event queue)</p>
  
  <p><strong>Why code outputs 2</strong></p>
  
  <p>after <code>console.log(4)</code> the main call stack is empty and javascript looks for the next callback to load on the main stack. It's pretty safe to assume that at this point, some "worker thread" had already put the callback function that outputs 2 onto the macro task queue. This gets loaded onto the stack and 2 is output. </p>
  
  <p><strong>Why code does NOT output 3</strong></p>
  
  <p>This is where its a little blurry for me. The function <code>three</code> returns a promise that is <code>then</code>-ed in the main thread. Callback functions passed through <code>then</code> are loaded onto microtask queue and executed before the next task in the macrotask queue. So while you might think it'll run before the callback that logs 2, its actually theoretically impossible for it to run at all. That's because the Promise is only resolved via the callback function of its setTimeout, and that callback function (because of setTimeout) would only run if the main execution thread (the same thread that's waiting for the promise to resolve) is empty. </p>
  
  <p><strong>Why does this bother me</strong></p>
  
  <p>I'm trying to build a complete theoretical mental model of how javascript handles concurrency. One of the missing pieces in that model is the relationship between network requests, promises, and the event loop. Take the above code snippet, and suppose I replace <code>three</code>'s setTimeout with some sort of network request (a very common thing in async web development). Assuming that the network request behaves similarly to setTimeout, in that when the "worker thread" is done, a callback is pushed to the macro task queue, it's hard for me to understand how that callback even gets executed. But this is something that happens literally all the time. </p>
  
  <p>Can someone help me understand? Do I have any missing gaps in my current understanding of js concurrency? Have I made an incorrect assumption? Does any of this actually make any sense? lol</p>