Understanding Asynchronous JavaScript

Uriel Rodriguez
3 min readMay 5, 2020

Transitioning from Ruby to JavaScript takes some getting use to. In Ruby, programs are written, and run, synchronously. Here’s an example of synchronous code in Ruby:

def my_function
puts 'Hello World'
end
def another_function
puts 'forgot the "!"'
end
my_function #=> Hello World
another_function #=> forgot the "!"

In this example, each function is called one after the other, and until the first function call puts ‘Hello World’, the second function will not execute its puts statement.

However, in JavaScript, things become a bit more complexed when asynchronicity is involved. Here’s another example of asynchronous code in JavaScript:

function fetchData(url) {
fetch(url)
.then(resp => resp.json())
.then(console.log)
}
function myFunc() {
console.log('Hello World!')
}
fetchData(someURL)
myFunc()
LOG: //=> Hello World!
LOG: //=> *fetched data

In this example, although fetchData was called first, myFunc logged to the console before it. So what happened? To better understand, it’s important to note that the fetchData function involves making a HTTP request via fetch, which belongs to the JavaScript Web APIs and exists outside of the JavaScript engine. To illustrate in steps involved in these two function calls:

  1. fetchData(someURL) is called
  2. fetchData(someURL) is placed in the execution stack
  3. but because it involves making a HTTP request, which is a functionality that exists outside of the JavaScript engine, it can and is moved to run asynchronously in the background, within the Web APIs environment
  4. as fetchData(someURL) is moved to the background to carry out its fetch, it is removed from JavaScript’s execution stack
  5. myFunc() is called
  6. myFunc() logs to the console ‘Hello World!’
  7. myFunc() call is removed from the execution stack
  8. some time between myFunc() is called and placed in execution stack and myFunc() logs to the console ‘Hello World!’, fetchData(someURL) receives the data from the fetch and is ready to execute its callback function which logs to the console the data returned
  9. unfortunately, the callback function cannot be placed directly in the execution stack, and is instead placed in the message queue where it waits until the execution stack is cleared (after myFunc’s console.log)
  10. once the event loop, which constantly monitors the execution stack and the message queue, finds the execution stack is cleared, it moves the callback function to the execution stack
  11. the callback function is executed and logs to the console the fetched data
  12. the callback function is removed from the execution stack

Within those two simple function calls, this entire process was being carried out. The asynchronous nature of the fetchData(someURL) call allowed for the execution to continue and not come to a halt until the data was returned from the HTTP request. Knowing how this process is carried out under the hood is useful when coordinating function calls, especially when the returned value of one is depended on by another. And just as a helpful reminder, fetch, DOM events (event listeners), XMLHttpRequest, setTimeout and a few others all execute asynchronously.

--

--

Uriel Rodriguez

Flatiron School alumni and Full Stack web developer.