The Node.js® Website
1--- 2title: JavaScript Asynchronous Programming and Callbacks 3layout: learn 4authors: flaviocopes, MylesBorins, LaRuaNa, amiller-gh, ahmadawais, ovflowd 5--- 6 7# JavaScript Asynchronous Programming and Callbacks 8 9## Asynchronicity in Programming Languages 10 11Computers are asynchronous by design. 12 13Asynchronous means that things can happen independently of the main program flow. 14 15In the current consumer computers, every program runs for a specific time slot and then it stops its execution to let another program continue their execution. This thing runs in a cycle so fast that it's impossible to notice. We think our computers run many programs simultaneously, but this is an illusion (except on multiprocessor machines). 16 17Programs internally use _interrupts_, a signal that's emitted to the processor to gain the attention of the system. 18 19Let's not go into the internals of this now, but just keep in mind that it's normal for programs to be asynchronous and halt their execution until they need attention, allowing the computer to execute other things in the meantime. When a program is waiting for a response from the network, it cannot halt the processor until the request finishes. 20 21Normally, programming languages are synchronous and some provide a way to manage asynchronicity in the language or through libraries. C, Java, C#, PHP, Go, Ruby, Swift, and Python are all synchronous by default. Some of them handle async operations by using threads, spawning a new process. 22 23## JavaScript 24 25JavaScript is **synchronous** by default and is single threaded. This means that code cannot create new threads and run in parallel. 26 27Lines of code are executed in series, one after another, for example: 28 29```js 30const a = 1; 31const b = 2; 32const c = a * b; 33console.log(c); 34doSomething(); 35``` 36 37But JavaScript was born inside the browser, its main job, in the beginning, was to respond to user actions, like `onClick`, `onMouseOver`, `onChange`, `onSubmit` and so on. How could it do this with a synchronous programming model? 38 39The answer was in its environment. The **browser** provides a way to do it by providing a set of APIs that can handle this kind of functionality. 40 41More recently, Node.js introduced a non-blocking I/O environment to extend this concept to file access, network calls and so on. 42 43## Callbacks 44 45You can't know when a user is going to click a button. So, you **define an event handler for the click event**. This event handler accepts a function, which will be called when the event is triggered: 46 47```js 48document.getElementById('button').addEventListener('click', () => { 49 // item clicked 50}); 51``` 52 53This is the so-called **callback**. 54 55A callback is a simple function that's passed as a value to another function, and will only be executed when the event happens. We can do this because JavaScript has first-class functions, which can be assigned to variables and passed around to other functions (called **higher-order functions**) 56 57It's common to wrap all your client code in a `load` event listener on the `window` object, which runs the callback function only when the page is ready: 58 59```js 60window.addEventListener('load', () => { 61 // window loaded 62 // do what you want 63}); 64``` 65 66Callbacks are used everywhere, not just in DOM events. 67 68One common example is by using timers: 69 70```js 71setTimeout(() => { 72 // runs after 2 seconds 73}, 2000); 74``` 75 76XHR requests also accept a callback, in this example by assigning a function to a property that will be called when a particular event occurs (in this case, the state of the request changes): 77 78```js 79const xhr = new XMLHttpRequest(); 80xhr.onreadystatechange = () => { 81 if (xhr.readyState === 4) { 82 xhr.status === 200 ? console.log(xhr.responseText) : console.error('error'); 83 } 84}; 85xhr.open('GET', 'https://yoursite.com'); 86xhr.send(); 87``` 88 89### Handling errors in callbacks 90 91How do you handle errors with callbacks? One very common strategy is to use what Node.js adopted: the first parameter in any callback function is the error object: **error-first callbacks** 92 93If there is no error, the object is `null`. If there is an error, it contains some description of the error and other information. 94 95```js 96const fs = require('node:fs'); 97 98fs.readFile('/file.json', (err, data) => { 99 if (err) { 100 // handle error 101 console.log(err); 102 return; 103 } 104 105 // no errors, process data 106 console.log(data); 107}); 108``` 109 110### The problem with callbacks 111 112Callbacks are great for simple cases! 113 114However every callback adds a level of nesting, and when you have lots of callbacks, the code starts to be complicated very quickly: 115 116```js 117window.addEventListener('load', () => { 118 document.getElementById('button').addEventListener('click', () => { 119 setTimeout(() => { 120 items.forEach(item => { 121 // your code here 122 }); 123 }, 2000); 124 }); 125}); 126``` 127 128This is just a simple 4-levels code, but I've seen much more levels of nesting and it's not fun. 129 130How do we solve this? 131 132### Alternatives to callbacks 133 134Starting with ES6, JavaScript introduced several features that help us with asynchronous code that do not involve using callbacks: Promises (ES6) and Async/Await (ES2017).