Do you Promise?

Steph O
4 min readJun 16, 2021

Javascript is single-threaded and synchronous.

Javascript has a single call stack from which a queue of code dictates the order of operations to run, running one function at a time; while blocking other processes from executing out of order. Upon a function’s completion, it’s then removed from the call stack. However, the call stack is limited in its capacity and Javascript can’t handle doing too much at once, continuously, in one shot.

Code can also run asynchronously.

As a sort of workaround, Javascript can seemingly run code asynchronously. It grants us the ability to implement processes that aren’t yet triggered to execute immediately but are reserved to run at a later time (while still technically executing synchronous code that’s running on a single thread). This is especially helpful for processes that can take longer to run and complete, like making a request to an API.

Javascript handles the HTTP pipeline behind the scenes, using #fetch.

When making HTTP requests to a server, we rely on the browser and one of its provided APIs, the Fetch API. It essentially sends the request over to the browser to be handled outside of the main call stack — which is how we’re able to run different functions at the same time asynchronously — without requiring page refreshes or navigation to different URLs (how Rails handles its request/response flow).

To make a GET request, we make a fetch request by invoking the #fetch method, provided to us by the browser which implicitly calls #fetch on the window object. The Fetch API takes the request, pulling it out of the main call stack to execute in its own, separate thread. The method takes in an argument of a URL to where we are making the request to (#fetch makes GET requests by default).

fetch(url)

Example in action:

let url = “http://xyz”
let request = fetch(url)
console.log(request)
=> Promise object

Whether successful or unsuccessful, requests’ return value is a Promise.

When we make a fetch request, it returns a Promise object (its return value). A Promise is an object that “represents the eventual completion (or failure) of an asynchronous operation and its resulting value.” What this means is that a promise is a placeholder that stands in for a value that is associated with an asynchronous function’s eventual value upon successful completion or reason for failure (aka, error). This lets asynchronous methods return values, just like how synchronous methods do; however, the values aren’t actually the final values, but rather, a promise to provide the final value at some later point in time. It’s essentially, an “IOU” that the function will attempt to provide the information requested; though it does not guarantee the attempt to be successful. As such, promise objects have three states that qualify the attempt’s resulting status of a request made:

  1. Pending: requests always start off with a state of pending (initial state); the request is neither fulfilled nor rejected because the information requested is not yet ready
  2. Fulfilled: the operation was completed successfully (#fetch found the data that was asked for)
  3. Rejected: the operation failed (error occurred)

As soon as the attempt to gather the information has been made, the state will change to either fulfilled or rejected depending on whether the request was successful or not. When either of these outcomes occur, the promise is resolved; the resulting object is a new, automatically-generated Response object that holds either information about what was requested or why the request failed.

The return values from #fetch and #then are more Promises.

We handle these objects that are generated for us from fetch requests using methods that call on that new Response object, like the #then, #catch, and #finally methods, which takes in a callback function as an argument. The callback function is not something we explicitly call to execute; it executes for us (by the #then method), similar to event listeners where callback functions are not explicitly invoked but rather invoked for us when triggered to. Since the #then method also returns another promise, we can chain more methods to handle the previously-resolved promise (aka, we chain #then methods on fetch requests).

When we get a response from the server, it includes a body that holds the information we asked for; it’s how we go from body → object, using the #json method.

Response objects generated have methods and properties, like the body of the response that holds the information requested. When a Promise resolves to a Response object, is when we can call methods, like #json on that Response object because its body property has methods. For example, body.json, where the #json method of the Body takes a Response object — the information in this body which is in JSON format — and converts it into a Javascript object (ie. array, string, etc.)

It returns its final return value of a Promise that resolves into a Javascript object (results from parsing the body text as JSON). It is important to note that though the method is called #json(), the result is not JSON; but actually, the result of taking JSON as the function’s input and parsing it to create this new Javascript object:

response.json().then(data => { fn })

--

--