Skip to main content

JavaScript Asynchronous

JavaScript Async Callbacks

Written by Published

An asynchronous callback is a function that runs once slow work has finished.

Before promises existed, this was the only way to handle a result that arrives later.

It works, but it nests badly once one job depends on another.

Example

Example

javascript

function loadUser(callback) {
  setTimeout(function () {
    callback("Ada");
  }, 10);
}

loadUser(function (name) {
  console.log("got " + name);
});

The output is got Ada, once the slow work has finished.

What is an Async Callback?

You pass a function in, and it is called when the work completes.

The result is given to the callback as an argument.

Nothing after the call can use the result, because it has not arrived yet.

Syntax

Syntax

javascript

function load(callback) {
  // later:
  callback(result);
}

The callback is called by the function, not by you.

The Result Only Exists Inside

Trying to return it does not work, because the function finishes first.

Example

Example

javascript

function loadUser(callback) {
  setTimeout(function () {
    callback("Ada");
  }, 10);
}

const result = loadUser(function () {});

console.log(typeof result);

The output is undefined: there was nothing to return.

The Error-First Pattern

By convention the first argument is an error, or null when all went well.

This is how Node.js callbacks have always worked.

Example

Example

javascript

function loadUser(callback) {
  setTimeout(function () {
    callback(null, "Ada");
  }, 10);
}

loadUser(function (error, name) {
  if (error) {
    console.log("failed");
    return;
  }
  console.log("loaded " + name);
});

Checking the error first means the happy path stays clear.

Reporting a Failure

The same callback handles both outcomes.

Example

Example

javascript

function loadUser(callback) {
  setTimeout(function () {
    callback("user not found");
  }, 10);
}

loadUser(function (error, name) {
  console.log(error ? "error: " + error : "loaded " + name);
});

The output is error: user not found.

Callback Hell

When one job needs the result of the last, the nesting grows sideways.

Three or four levels in, this becomes very hard to follow.

Example

Example

javascript

function step(name, callback) {
  setTimeout(function () {
    callback(name);
  }, 5);
}

step("one", function (a) {
  step("two", function (b) {
    step("three", function (c) {
      console.log(a + " " + b + " " + c);
    });
  });
});

Promises exist mainly to flatten this shape out.

Named Functions Help a Little

Pulling each level out reduces the nesting, but not the awkwardness.

Example

Example

javascript

function step(name, callback) {
  setTimeout(function () {
    callback(name);
  }, 5);
}

function afterSecond(b) {
  console.log("finished with " + b);
}

function afterFirst(a) {
  step("two", afterSecond);
}

step("one", afterFirst);

The real fix is promises, which the next lesson covers.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Async Callbacks</title>
</head>
<body>

  <h1>Async Callbacks</h1>

  <p id="out">Loading...</p>

  <script>
    function loadUser(callback) {
      setTimeout(function () {
        callback(null, "Ada Lovelace");
      }, 1000);
    }

    loadUser(function (error, name) {
      const out = document.getElementById("out");
      out.textContent = error ? "Failed: " + error : "Welcome, " + name;
    });
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try an error:

Change the call to callback("no network") and see the other branch run.

Important Points

  • A callback runs when the slow work has finished.
  • The result cannot be returned, only passed to the callback.
  • The error-first pattern puts the error in the first argument.
  • Nesting callbacks quickly becomes hard to read.
  • Promises were created to solve exactly this problem.

Conclusion

Callbacks are the oldest way of handling asynchronous results, and still everywhere.

Understanding them explains why promises look the way they do.

The pain of nesting is what the next few lessons remove.