Node.js and Asynchronous Javascript

These are my notes for Jonas Schmedtmann’s Node.js Bootcamp on Udemy: https://www.udemy.com/course/nodejs-express-mongodb-bootcamp/
Promises
Writing asynchronous Javascript with callback functions is all fun and games until we get deep nested layers of callback functions that are difficult to maintain. Below is a program that fetches dog pictures from an API. Imagine this mess but with another dozen layers:
To avoid callback hell, we use other aysnc techniques, such as promises. Some terminology: a promise that has yet to return data is called a pending promise, one that has returned data is called a resolved promise. Resolved promises, in turn, can either be fulfilled (came back with the desired data) or rejected (came back with an error). The above code rewritten with a promise looks like this.
It’s not a huge change so far, but we’re not done yet. Notice that the then() method has no err parameter. Actually, .then methods don’t have access to the error object, as they only get called with fulfilled promises. Instead, promises use the catch() method, which we chain onto the .then method:
Next, we can build a promise for our file read. When we build a promise, we pass it an executor function, and to that function we pass two arguments: resolve and reject. The resolve argument passes its data on to the then() method, and the reject argument passes its data (i.e. error message) to the catch() method.
We’ll do the same for our file write. A file write just performs an action and doesn’t return any meaningful value, so we just pass "success" into resolve().
When we chain promises, we need to return a promise (which superagent does implicitly) and then chain a then method which returns another promise, etc. At the end of the chain, we need only one catch method. The end result:
Async / Await
Using async/await syntax, we can do away with our promise chain. Once we’ve marked a function as async, we can use the await keyword to say “stop function execution until this promise resolves.” This syntax looks like:
Problem is, the above code doesn’t do error handling. Therefore, we need to add a standard Javascript try block and catch block:
Suppose we wrote some console logs before and after our asynchronous function:
We might expect this sort of output:
1: Will get dog pics!
*** getDogPics()'s console logs here ***
2: Done getting dog pics!What actually happens is:
1: Will get dog pics!
2: Done getting dog pics!
*** getDogPics()'s console logs here ***The getDogPics() console logs come last because it is an asynchronous function that runs in the background.
We can make things even weirder by returning a string from getDogPics():
Our console logs now appear in the right order, but they look like this:
1: Will get dog pics!
Promise { <pending> }
3: Done getting dog pics!We see a promise because async functions return promises automatically. When we assign x, the promise has not yet been resolved. To get the console logs in the correct order, we can use the then() method:
Error handling gets tricky in this scenario. Because the getDogPic promise is resolved whether it is fulfilled or rejected (resolved =/= fulfilled), it will pass our return value into the then() method. If we want to pass the error to the catch block, we have to throw it:
Often times, we don’t want to mix async/await syntax with then/catch syntax. We can rewrite the then/catch block by using an IIFE and async/await:
This code prints our console logs as expected.
Multiple Promises
What if we wanted three dog pictures? We could write something like this:
The problem is, the second and third superagent calls have to wait for the one before it to finish before starting. To get these promises all running simultaneously, we can make an array of them and pass that array into Promise.all(). Not that are storing the promise itself, not the result, in a variable, so we remove the await keyword.
Because we’re only interested in the .body.message value of each promise, we’ll modify our array with a map() function, and then write the joined array to our output file:
