Internet
Seminar

Spring 2019
course
site

Callback Functions

Resources

General Description

Definition from MDN: A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

example:

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('Please enter your name.');
  callback(name);
}

processUserInput(greeting);

In the above example, a function, greeting(name) is defined as a function that takes an argument name, and shows an alert('Hello' + name). processUserInput is defined as a function that takes an argument, and calls that argument as a function with an argument defined inside processUserInput. processUserInput is then called with the function greeting passed as an argument. This will prompt the user to input their name, then greet them with an alert saying hello (name). MDN goes on to say that this is a synchronous callback function but callback functions are most often used asynchronously. They are often seen in the callback function executed inside of .then() blocks after a promise resolves... for instance in the fetch API.

Fetch API

Fetch is a method from the WindowOrWorkerGlobalScope mixin that is used to request resources from a network. It takes a request object that includes the location of the resource and headers and returns a promise that resolves as a response object. Fetch rejects when there's a network error but doesn't reject when there's an http error (404, 500, etc.). To check the response status one must use .then() to handle for the response.ok or response.status properties of the response object. Here's the MDN example:

const myImage = document.querySelector('img');

let myRequest = new Request('flowers.jpg');

fetch(myRequest)
.then(function(response) {
  if (!response.ok) {
    throw new Error('HTTP error, status = ' + response.status);
  }
  return response.blob();
})
.then(function(response) {
  let objectURL = URL.createObjectURL(response);
  myImage.src = objectURL;
});

In this case we are requesting an image, handling for any http errors, then processing the image and displaying it on the page. The .then() handlers chain together by each returning a modified promise or handling for an error. If an error is thrown, the chain is interrupted, and future .then() statements are skipped until a .catch statement is found. This isn't present in the above code, but I've included an example from my code that handles a bit more for error messages.

let id = this.props.id
    fetch(`/api/v1/celestials/${id}.json`,{
      credentials: 'same-origin',
      method: 'GET',
      headers: { 'Content-Type': 'application/json'}
    })
    .then(response => {
      if (response.ok) {
        return response;
      } else {
        let errorMessage = `${response.status} (${response.statusText})`,
        error = new Error(errorMessage);
        throw(error);
      }
    })
    .then(response => response.json())
    .then(response => {
      console.log(response)
      this.setState({ reviews: response.reviews, current_user: response.current_user })
    })
    .catch(error => console.error(`Error in fetch: ${error.message}`));
  }

In this case we are making a request for a json object with some headers attached, handling for an http error, then changing the state of my app to mirror the json object.

Javascript Promises and Callback Hell

It use to be that every asynchronous callback function would need a second function to handle a failure case. So doing something would look like this:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

Now we do something like this, chaining promises upon resolution:

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

or with arrow functions:

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);