Callbacks and Promises — When Javascript Plays Hard to Get

Julia Zolotarev
The Startup
Published in
5 min readNov 29, 2020

Plunging into Javascript after spending time with Ruby feels like diving into the deep end of a pool after splashing around the kiddie section with floaties on. Gone are the expressive errors I’ve come to love and the minimalistic curly-less method definitions.

With Javascript came a handful of concepts that either didn’t exist in Ruby, or I somehow had not previously found confusing. One such concept is that of callback functions.

In theory, callback functions are very straightforward if you have some basic knowledge of writing code. A function is simply a set of instructions bundled into a package that you define and then can be executed (called/invoked) at a specific time.

A callback function is a such a block of code that is passed to another function as an argument. The important thing to remember here is that a function executes only when it is invoked; not when it is defined. I’m going to give a very contrived example for the purpose of this demonstration:

3 functions

I’ve defined 3 functions here: saySomething takes in an argument of some string and logs that string to the console.makeArray takes a string, turns it into an array, then logs it to the console.saySomethingElse takes in an argument of greeting, name and a callback function, turns the greeting and name into a string, and then performs the callback function action upon that string.

You could use the first two on their own of course, but the third will not work properly unless you invoke it with a function as the third argument.

Running saySomethingElse(“hello”, “Julia”, makeArray) in the console will log the array (2) [“hello,”, “Julia”]. Running saySomethingElse(“hello”, “Julia”, saySomething)will log the string“hello, Julia”.

Notice that makeArray and saySomething are not being passed into the function call with any argument of their own, nor are there parentheses at the end to invoke the function. As it stands now, the makeArray function and the saySomething function will not execute until I invoke the saySomethingElse function with one of them passed in.

If I passed either of them in with parentheses (e.g saySomethingElse(“hello”, “Julia”, makeArray())), Javascript would interpret that as invoking the makeArray function and returns the error “Uncaught TypeError: Cannot read property ‘split’ of undefined” since we didn’t pass anything into the makeArray function.

If we tried to fix that error by passing an argument into the makeArray function (e.g. saySomethingElse(“hello”, “Julia”, makeArray("testing a theory"))), the behavior is still not what we want. Javascript interprets this as us calling on the makeArray function with an argument of the string “testing a theory” and logs (3) [“testing”, “a”, “theory”] to the console, along with the error “Uncaught TypeError: callbackFn is not a function,” basically telling us that we tried calling on our saySomethingElse function without a callback function passed in.

A common use case for callback functions in Javascript (there are a LOT) is handling events.

event listener functions

Lines 18–20 represent an event listener on a button, listening for a click event, and then using some anonymous function that’s defined in-line to do some action.

Alternatively, you could write a function specifically designed to handle a button click (as on lines 23–25), and then use that function as a callback in the event listener defined on line 27. Both are perfectly valid and useful in different scenarios. If there is a lot of logic inside the the button click, it is better for readability to separate those; similarly if you will reuse the logic for the button click elsewhere, it should live inside its own function.

The part that was confusing for me was the event. The event is an optional parameter in event listener callback functions that represents the event that took place and contains the properties and methods for that event. “But how does the handleButtonClick function know about the event?” is the part I could not understand. It’s being defined separately from the event listener which will actually tell us what the event object is, right?

As it turns out, the logic is simple: the optional callback function (like the one I defined on line 23) has the same parameter and return value as the generic unnamed function in the example on line 18. Once it’s inserted into the event handler as a callback function, it will automatically have access to the event parameter. That’s it! As before, we need only to write a reference to the function in the event handler arguments — not an invocation of the function. We need that function to run only when the button is clicked, not at the compiling of the JS file.

Hopefully that cleared a few things up about callbacks, which will free us up to talk about javascript promises. What is a promise, you ask? It is an object that represents the eventual completion or failure of an asynchronous operation, as well as its resulting value. Whereas the examples we looked at above utilized callback functions as arguments in other functions, one of the cool things about promises is that callback functions can be chained onto promises, rather than being passed in.

An example of an action that would return a promise is a fetch request. (A fetch request is a way to use Javascript to communicate with a server or an API). It would look something like this: fetch(“http://localhost:3000/bakes")

Let’s look at the diagram below. Once our example fetch to localhost:3000/bakes fires off, we have a pending promise. It is either fulfilled or rejected (for this example let’s say it’s fulfilled) and we call the .then function to parse the returned promise. This first .then also returns a promise (a different one), so we call .then again until we get to the value we need.

flowchart of what happens after promise in JS

The nice part of these asynchronous promises is that the chained methods will wait for the promise to finish resolving and only run once the previous operation has succeeded.

There is much more to learn on promises, but this is a good start, I promise!

I recommend my colleague’s blog post on promises if you want a bit of a deeper dive: https://james-ardery.medium.com/.

--

--

Julia Zolotarev
The Startup

Software Engineer; hospitality enthusiast; lover of ice cream.