Asynchronous JavaScript and Callbacks 

Objectives

  • Store and use anonymous functions in variables.
  • Pass functions as arguments to functions that expect them.
  • Write functions that take other functions as arguments.
  • Return functions from functions.

Review

  • Implement an AJAX request with Vanilla JS.
  • Reiterate the benefits of separation of concerns – API vs. Client.
  • Understand the difference between function declarations and function expressions.
  • Understand scope and hoisting.

Introduction to Asynchronous JavaScript

As our programs start relying on user input/behavior and data that might not be available right away, we need to increasingly start thinking about how we can run our code at different times.

 

We refer to this "now and later" programming as asynchronous programming. 

We have already used asynchronous programming in our code in previous lessons:

  • We have listened for click and mouse events that execute certain code when that event is complete.
  • We have waited for a return from an AJAX call, and we have executed code for success and error scenarios.

 

Although we have used asynchronous programming in our code, we have not discussed how this is all happening. Before we can truly understand how asynchronous programming works in JavaScript, we need to take a deeper look at functions and scope.

Anonymous Functions and Functions as First-Class Objects: Recap

We have worked with numerous scenarios where we have run a block of code after a user has performed an action.

var $thingList = $('#fav-list');

  $thingList.on('mouseleave', 'li', function(e) {
    ...
  });

Taking a close look at the jQuery on() method, we notice two very important overlooked concepts:

  1. The on() method is taking a function as one of its parameters.
  2. The function we are passing into the on() method does not have a name, we are just passing a raw function

Functions are First-Class Objects in JS

They can be used in any part of the code that strings, arrays, or data of any other type can be used

 

This means we can store functions as variables, pass them as arguments to other functions, return them from other functions, or just run them ad-hoc without the need to assign them to anything.

 setTimeout( function(){
    console.log("Hello world");
  }, 1000 );

A function that takes another function as an argument, or returns a function, is called a higher-order function.

 var blastOff = function() {
    console.log("Blasting off!");
  }

  function launchRocket(rocketName, blastOffCallback) {
    console.log("Launching " + rocketName);
    console.log("3... 2... 1...");
    blastOffCallback();
  }

  launchRocket("Viking", blastOff);

  // => Launching Viking
  // => 3... 2... 1...
  // => Blasting off!

In this example, the function being passed into launchRocket (blastOff) is acting as a "callback", which is a function that is designed to be executed by the code of another function.

  var blastOff = function(destination) {
    console.log("Blasting off for " + destination + "!");
  }

  function launchRocket(rocketName, blastOffCallback) {
    console.log("Launching " + rocketName);
    console.log("3... 2... 1...");
    blastOffCallback("Mars");
  }

  launchRocket("Viking", blastOff);

  // => Launching Viking
  // => 3... 2... 1...
  // => Blasting off for Mars!

Callback functions can also take arguments, even though we don't specify the need for arguments when we're passing the callback function itself as a variable

  var blastOff = function(destination) {
    console.log("Blasting off for " + destination + "!");
  }

  function makeRocketLauncher(rocketName, blastOffCallback) {
    return function() {
      console.log("Launching " + rocketName);
      console.log("3... 2... 1...");
      blastOffCallback("Mars");
    };
  }

  var launchViking = makeRocketLauncher("Viking", blastOff);
  var launchMariner = makeRocketLauncher("Mariner", blastOff);

  launchViking();
  launchMariner();

  // => Launching Viking
  // => 3... 2... 1...
  // => Blasting off for Mars!

  // => Launching Mariner
  // => 3... 2... 1...
  // => Blasting off for Mars!

Just as we can pass functions as arguments to other functions, we can also return functions from other functions:

Functions and Callbacks: Independent Practice

Open the main.js file.

  • Write a function, makeCountingFunction(), that returns another function.
    • The function returned by makeCountingFunction() should take an array as an argument, and return the number of odd integers in the array.
  • makeCountingFunction() itself should take as its only argument something called a "predicate function", a function designed to run a test on each item in an array.

 

Test your code by running it in the command line using node!

Anonymous Functions and Immediately-Invoked Function Expression

Function being passed as callback argument

  setTimeout( function(){
    console.log("Hello world");
  }, 1000 );

The function being passed into setTimeout() above is called an anonymous function expression because it is not named in any way -- it is not a function expression that got assigned to a variable name, and neither is it a function that got its name in a function declaration

Note that you don't have to pass anonymous functions as callbacks in cases like this -- you can pass named functions as well, no matter how they got their names:

 

function sayHi() {
    console.log("Hello world");
  }

  var sayBye = function() {
    console.log("Goodbye world");
  }

  setTimeout(sayHi, 1000 );
  setTimeout(sayBye, 1261440000000);

  // (1,261,440,000,000 milliseconds is 40 years)

Note: Anonymous functions are great, but they may give you some trouble when debugging, if you're looking at a stack trace and it shows a big column of unnamed functions. To get around this you can name them as shown above, or you can use a third way to give them a name which will show up in debugging, called a "named function expression":

 

setTimeout(function timer(){
    console.log( "Hello world" );
  }, 1000 );

We have the ability to execute our function expressions as soon as they are declared. This pattern is very commonplace: Immediately-invoked function expressions (IIFE).

 

We are familiar with this syntax:

  var countDown = function() {
    var counter;
    for(counter = 3; counter > 0; counter--) {
      console.log(counter);
    }
  }
  var countDown = function() {
    var counter;
    for(counter = 3; counter > 0; counter--) {
      console.log(counter);
    }
  }();

To make an IIFE and execute the function expression immediately adding parenthesis at the end of the expression:

 

IIFEs are just functions, so we can also pass arguments to them:

var counterStartPoint = 3;

  (function countDown( global ){
    var counter;
    for(counter = global.counterStartPoint; counter > 0; counter--) {
      console.log(counter);
    }

  })( window );

  console.log( counterStartPoint ); // 3

Immediately-Invoked Function Expression: Independent Practice

Open the main.js file.

  • Write an IIFE function that takes a timer argument.
  • The function will automatically execute and count up every second until the specified argument.
  • Use the setTimeout function to count up.
  • Hint: a second is the timer passed * 1000 (milliseconds).

Conclusion

Callbacks and closures are the bread and butter of asynchronous programming. Looking back at our DOM and APIs lessons, our interfaces update on user interactions and/or once we receive data from remote locations. Best practices in JS call for these reactions to be handled in the form of callbacks.

 

Callbacks, closures, and IIFEs allow us to better organize our code for each scenario, as well as make our functions significantly more dynamic.

Homework

Sign up for a 500px developer account.  Review the set of sign up instructions to help.

 

Optional homework: Students who want an additional challenge can try to complete this series of functional-programming JavaScript exercises.  Some of these exercises use methods and syntax that we haven't covered in class and which you will likely need to look up on your own.

Further Resources

Aug 22: Asynchronous JavaScript and Callbacks

By Jessica Bell

Aug 22: Asynchronous JavaScript and Callbacks

  • 157