DEV Community

Cover image for Callback Functions - JavaScript Core Concepts
Angeline Wang
Angeline Wang

Posted on

Callback Functions - JavaScript Core Concepts

The Section 2: Functions & Methods of my JavaScript Core Concepts Series is divided into 6 Parts:

Part 1 Functions
Part 2 Classes
Part 3 Methods
Part 4 Callback Functions
Part 5 Scope
Part 6 Hoisting

Callback Functions

Part 1 What is a Callback Function?

= Callback: Name of a JS Function Convention
→ Not a tool w/ unique functionality

Here are a few definitions:

  1. Function executed
    = At end of execution of another function

  2. Function passed into another Function as an Argument
    = To be executed later

  3. Function that runs asynchronously

Functions = Objects in JS
= They can take Functions as Arguments
→ & Can be returned by other Functions

Difference between Callbacks & most Functions
= Callbacks do not immediately return some result
→ They take time to product a result

Higher-Order Function

= Function that takes other functions as arguments
→ Function being passed in: Called a Callback Function

Code Example
const button = document.querySelector(‘button’)
button.addEventListener(‘click’, function(e) {
    this.classList.add(‘clicked’)
})
Enter fullscreen mode Exit fullscreen mode

= JS listening for click event on button
→ Only calls the function in 2nd argument if event is detected

Part 2 What is the purpose of a Callback Function?

  1. Store things to do at later time
    = Order in which events occur: Does not work top-to-bottom
    → Skips around depending on when things complete

  2. Dealing with tasks that get dispatched
    = Go off and do something in the background
    → Then, complete successfully or abort due to failure

Callbacks are used in 2 different ways:

1st Way: Synchronous Functions

= Code is synchronous
→ If executes top-to-bottom, left-to-right, sequentially
→ Waiting until 1 line of code finishes
Before next line begins
→ Process continues until last line of code is executed

Callbacks in Synchronous Functions

= When want a part of code to be easily swapped w/ something else

Code Example
const numbers = [10, 11, 19, 40]
const getLessThanFifteen = num => num < 15
const getMoreThanTwenty = num => num > 20 

const lessThanFifteen = numbers.filter(getLessThanFifteen)

const moreThanTwenty = numbers.filter(getMoreThaTwenty)
Enter fullscreen mode Exit fullscreen mode
Normal vs Async Functions

Normal Functions
= Allow you to use its return value

Async Functions that use callbacks
= Don’t return anything straight away

Waiting for Response
= Don’t want program to pause
→ So you store the code that should run afterwards
Inside a Function: Called a Callback Function

2nd Way: Asynchronous Functions

= Takes some time, happens in the future, not right now
→ JS needs to wait for something to complete
→ Will run rest of tasks given to it: While waiting

Code Example

= Asynchronous Function that takes a Callback to execute at later time

const twentySecondsLater = () => console.log(‘20 seconds passed!’)


setTimeout(twentySecondsLater, 20000)
console.log(‘Start!’)
Enter fullscreen mode Exit fullscreen mode

= JS executes setTimeout
→ Then it waits for 20 seconds
→ And then logs ‘20 seconds passed!’

= Same time as waiting for setTimeout to complete in 20 seconds
→ JS executes `console.log(“Start!”)

Part 3 How do you create a Callback?

= There are 2 ways to create Callbacks:

1st Way: Anonymous Functions

  1. Pass Callback Function Definition as an Argument

  2. Define it in the Function Call

  3. To do this, you pass in callback as the last Parameter of the Function

  4. Then the Callback Function itself is defined in the 2nd Argument during the Function Call

Code Example


function createEvent(date, callback) {
console.log(
The event on ${date} has been created.`);
callback();
}

createEvent(“September 22nd, 2022”, function() {
console.log(The event is being displayed.)
})
`

2nd Way: Named Functions

  1. Pass reference to Callback as an Argument

  2. Or you can define the Callback Function elsewhere
    = & Pass its reference as the 2nd Argument during the Function Call

  3. You can also pass in any amount of arguments that the callback may need
    = & Arguments passed into Callbacks
    → Make their way through to the Callback

Code Example


function createEvent(date, callback) {
console.log(
The event on ${date} has been created.`);
callback();
}

function displayEvent(){
console.log(The event is being displayed.);
}

createEvent(‘September 22nd, 2022’, displayEvent);
`

Part 4 What do Callbacks look like in-action?

= Asynchronous Callbacks are useful as a Solution to Blocking Behavior

= Usually used when there is a transfer of data when doing I/O
→ ie Downloading things, reading files, talking to database, etc.

Callback Queue

= Everything in the Stack is ran before moving onto the Callback Queue
→ Anything in a setTimeout() is ran with the Callback Queue
→ This means that even if something in the Callback Queue is never completed, the code keeps running

Is the Callback Queue for all Callbacks or only Async Callbacks?

Common Use Cases

  1. When an event fires
    = Event listeners
    ie addEventListener

  2. After AJAX Calls
    = jQuery’s ajax method
    ie jQuery.ajax

  3. After reading or writing to files
    = Node, ExpressJS
    ie fs.readFile

Purpose of Callbacks

= Ensure response of Function is received/finished executing
→ Before executing other code

Example Use Case

= Make Requests to an API
→ Need to wait to receive a Response from Server
To act based on Response
→ Do not know if API Request is going to be successful

Code Example


T.get(’search/tweets’, params, function(err, data, response) {
if(!err){
// What to do if API request successfully executes
} else {
console.log(err);
}
})

= After Response received
→ Callback is invoked
→ Twitter will either send an err Object
Or a response Object back to us

= Conditional w/in Callback
→ Uses Information
To determine if Request was successful or not
Then act accordingly

Part 5 What are the problems with Callbacks?

The biggest problem with Callbacks is Callback Hell

What is Callback Hell?

= Multiple Callbacks nested after each other
→ ie Doing an asynchronous activity
That depends on previous asynchronous activity

Example Scenario

= Usually, Callback Hell is found in the Backend
→ While using Node
→ Rare to see Callback Hell in Frontend JavaScript

= Pyramid shape with numerous })s at the end
→ Result of attempts to write JS so execution occurs chronologically
→ In JS, what occurs on 1st line will not necessarily finish before code in next line starts running
ie Async functions, setTimeout()s (which get ran after the call stack)

Why are Nested Callbacks bad?

= Code much more difficult to read

What is the solution to Callback Hell?

= To prevent Callback Hell, you can do these things:

1. Write Shallow Code

#1 Do not nest Anonymous Functions
= Name all Functions
→ Use Functions through reference to Names
→ Rather than writing out elaborate logic of anonymous function

→ Code will be easier to understand → B/c descriptive Names

If exceptions occur
= Have Stack Traces w/ Function Names
→ Bugs are easier to investigate

#2 Place all Functions at top-level program (‘below the fold’)
= Take advantage of Function Hoisting
→ Move all Functions to bottom of File
→ Then, move into another File: When & If the load demands

2. Modularize

#1 Write small Modules
= That each do 1 thing
→ Compile them into other Modules that do a bigger thing
→ ie Split code into couple of Files

#2 Isolated Files
= Used by loading them through a relative require()
→ Then, can move Functions into standalone Module

Code Example
= Create a new File for a series of Functions
→ Performing related functionality

ie Module named useruploader.js

`
module.exports.create = userCreate

function userSubmit (submitEvent) {
var name = document.querySelector(‘input’).value
request({
uri: “http://example.com/upload”,
body: name,
method: “POST”
}, postResponse)
}

function postResponse (err, response, body) {
var statusMessage = document.querySelector(‘.status’)
if (err) return statusMessage.value = err
statusMessage.value = body
}
`

module.exports

= Example of Node.js Module System
→ Works in Node, Electron & Browser

Benefit of module.exports

= This style of Modules is good because it works everywhere
→ Easy to understand
→ Does not require complicated config files or scripts

Using module.exports Modules

= After File is created
→ You need to require it
→ & Use it in your application specific code

Code Example


var userUploader = require(‘useruploader’)
document.querySelector(‘form’).onsubmit = userUploader.submit

Benefits of Modularising

1. Comprehensibility

= Code required in app is only 2 lines
→ Allows new devs to more easily understand you code
→ Won’t need read through details of all Functions w/in Module: When trying to grasp what Program is trying to do

2. Error-handling, test-writing, creating a stable and documented public API
3. Refactoring
4. Reuse

= W/o duplicating code
→ Can easily be shared on Github or npm

How do you create good Modules?

1. Move repeatedly used code into a Function
2. When Function/group of Functions gets large enough

= Move them into another File
→ Expose them using module.exports
→ Which can be loaded w/ a relative require

3. Code that can be used across multiple projects

= Give it a Read.me, tests, and package.json
→ Public it to Github & npm

4. A Good Module

= Small & focuses on 1 problem

5. Module Should Not

= Have > 1 level of nested Folders full of JS Files
→ If it does: It is likely doing too many things

6. Reference Examples of Good Modules

= Get a good idea of what they look like

7. If Module takes > a few minutes to understand

= Not Good

Part 6 How should Callbacks be used?

= Write Stable Code
= Handle Every Error
→ You should handle every single error
→ In Every One of your Callbacks

How can you handle every error?

a. Plan on errors always happening

= Can never know when Errors happens
→ Must make code stable

b. Node.js Style

= 1st Argument to Callback
→ Always reserved for an Error
→ Use error as 1st Argument Convention
→ Instead of adding it as the 2nd Argument

= Reminds you to handle your errors

c. Code Linters

= Help remind you to handle Callback Errors
→ Show you every Callback in your code
→ With an Unhandled Error

Types of Errors

#1 Syntax Errors

= Caused by Programmers
→ Usually caught when trying to 1st run program

#2 Runtime Errors

= Caused by Programmer
→ Code ran, but had a bug that caused something to mess up

#3 Platform Errors

= Caused by
→ ie Invalid File Permissions, Hard Drive Failures, no Network Connection etc.

...

Resources for Further Exploration:

JavaScript: What the heck is a Callback? By Brandon Morelli

Callback Hell: A guide to writing asynchronous JavaScript programs

What the heck is the event loop anyway? | Philip Roberts | JSConf EU

Top comments (0)