WebCake

This is the first of a two-part series. You can find the second post here.


One of the coolest features of ES6 is its built-in support for promises.  Unlike the way we’ve been developing Node modules, the promise API allows us to build an async workflow that aligns to the way many of us have been developing front-end JavaScript applications, for example in Angular, which relies on handling async calls in our main workflow, instead of deep within a mess of callbacks.

Intro

Promises allow us to write asynchronous functions that are evaluated on their return.  Without getting too deep into it, their purpose is to add processes to our JS application that don’t block the main thread.  For more on promises, and how/when to use them, I highly recommend this article by Nolan Lawson from PouchDB. The more you start to work with them, the more clear their benefits become.

About This Demo

This is a very simple demo showing how to create an async workflow that will run an async file system call, and then give feedback once it’s done.  The goal is to show you how I’ve used promises, and not to show you exactly when you might use them. Although personally I’ve started to use them anywhere I write an async function.

That said, today we’ll be building a very simple application in Node that uses the fs module to write a file, and then write a message to the console.  Though this is an extremely simple use case, Node works in almost entirely asynchronous functions; use cases for promises in Node development almost inherently present themselves the more you work with Node.  The goal here is to simply show you how to set them up.

Getting Started

First and foremost, make sure you’re running Node v4.0.0 or higher, as you’ll need one of the more recent versions to make use of ES6 promises.  If you’re on a Mac, you can run the following command (provided you have Homebrew installed, which yes, you should):

$ brew update
$ brew upgrade node

That will get you the latest version of Node, which at the time of this writing is v5.1.0.  As for Windows, I’m sorry, I haven’t used a PC in a long time.  I suggest Google will have some solid instructions.

Create an empty directory in your dev environment where we can create some files.  In fact, today we’ll create two files.  The first will be created in your IDE, and the other will be built by running our simple Node application.

Start a file called app.js, and add the following:

'use strict';

const
    fs = require('fs');

// we'll call our async function here 

// we're write our async function here

That will be the base of our application. We’ll write a private function next, which will contain the guts of our promise-based demo.

If you’re not familiar with it, the const declaration that you see above is a constant variable. It’s new to JavaScript, introduced in ES6, and declares variables that cannot be changed, also known as immutable variables. They’re great for pulling in Node modules, since those are variables you’re super unlikely to ever modify. As for the 'use strict', at the time of this writing Node will throw errors on certain ES6 features if not written in strict mode. Besides, like abundant unit testing, it’s something we should be doing anyway.

Returning Promises

We’re going to write a function that will return a promise, which will run the asynchronous fs call to write our file. The advantage here is that we can let our async function run independently and not worry about having to tie ourselves to a callback. While in the example here that might seem trivial, once you apply that thinking to a larger set of processes, or a series of async calls, it becomes much clearer.

The order of where we put things in this file doesn’t really matter (with the exception of the const, mind you), but I like to keep business logic near the top, and functional logic near the botttom; so if you’re following my pattern, at the bottom of the file (where the comment above says we’ll write our async function) we can add the following function call:

function writeFile(fileName) {
    return new Promise((resolve, reject) => {
        // our fs call will go here...
    });
}

Let’s look at what’s going on. We’ve added a function that returns a new Promise, with a function that will be executed when the promise is triggered. That function takes two parameters, a resolve method, and a reject method. Those represent the two possible graceful results of firing our async call – either it succeeded, or it didn’t.

Resoliving and rejecting

Let’s add our call to the file system:

function writeFile(fileName, fileContent) {
    return new Promise((resolve, reject) => {
        fs.write(__dirname + fileName, fileContent, err => {
            if(err) {
                reject('We all had stuff to do, but I was given an exception');
            } else {
                resolve('Mission accomplished');
            }
        })
    });
}

Here we have a function that will write a file to the current directory, by passing the fileName and fileContent into our fs.write() method. This gives us two possible outcomes to handle, which we address in the third argument, the callback function. The first – there’s an error in the write process, and we handle it by rejecting the promise. The second outcome is the one we intend to see – there was no error, and the async call returned successfully.

You’ll notice that both the resolve and reject functions take arguments, and in the example above we’re just passing strings. You might also pass the error object, or any return data that comes from your async call – e.g. if you were reading a file instead of writing one.

Calling Promises

Replace the comment about calling our async function with the following:

writeFile('cake.txt', 'The cake is a lie!').then(successMsg => {
    console.log(successMsg);
}, errMsg => {
    console.log(errMsg);
});

In this example, we’re calling a single promise with some hard-coded data to write to the file system. You might notice that the promise has a .then() chained to it, which, if you’re familiar with Bluebird or Q, you’ve certainly seen before. This method handles promise resolution by taking two arguments, both callbacks. The first argument handles the resolve, while the second handles the reject. Notice that I’m explicitly stating which is which by anticipating the tone of the string that’ll be returned. In a more abstract method call, you might write something more generic, but this was done for demonstration purposes.

Run that demo!

Navigate your command line to the project folder, and run the following:

$ node app.js

This will trigger Node to run your module. If your project ran successfully, you should see a new file in your project directory, cake.txt, and your console output should say Mission accomplished. Here’s what my finished code looks like:

'use strict';

const
    fs = require('fs'),
    process = require('process');

writeFile('cake.txt', 'The cake is a lie!').then((successMsg) => {
    console.log(successMsg);
}, (errMsg) => {
    console.log(errMsg);
});

function writeFile(fileName, fileContent) {
    return new Promise((resolve, reject) => {
        fs.writeFile(fileName, fileContent, err => {
            if(err) {
                reject('We all had stuff to do, but I was given an exception');
            } else {
                resolve('Mission accomplished');
            }
        })
    });
}

That’s a good starting point with promises in Node. In another post, we’ll talk about calling multiple promises, and the running a function once they’re all complete.

One response to “ES6 Promises in Node”

  1. […] This is the second of a two-part series. You can find the first post here. […]

Leave a Reply

Your email address will not be published. Required fields are marked *