Okay, so we understand that:
So how does the actual concurrency work? In the case of:
setTimeout(() => {
console.log("Hi I'm async!");
}, 1000);
What logic makes sure that the callback function isn't pushed into the task queue until 1000 milliseconds have passed? Or regarding an HTTP request, what logic pushed the network response into the task queue when the request is complete?
The answer is external APIs. Things like setTimeout, fetch, and addEventListener are all examples of external APIs that the browser or Node.js, Deno, or Bun provide – they are not part of the core JavaScript language.
The JavaScript runtime (your code and the JS engine) is single-threaded, but these external APIs are not! The host environment can run them in the background (often on separate threads or system-level services), and when they're done, the host environment pushes their results into the task queue for the event loop to handle.