DEV Community

Manuel Romero
Manuel Romero

Posted on • Edited on

From callbacks to fs/promises to handle the file system in Node.js

In this post, I will show you the evolution of Node.js to work with the file system.
Let's start by creating a file:

const fs = require("fs");

fs.writeFile("/tmp/test.js", "console.log('Hello world');", error => {
    if (error) console.error(error);
    else console.log("file created successfully!");
});
Enter fullscreen mode Exit fullscreen mode

If we wanted to avoid callbacks, before Node.js v8 we had to manually promisify the fs.writeFile function or using third party modules such as bluebird or Q.

Let's promisify manually and wrap in a function the above code:

const fs = require("fs");

const writeFilePromise = (file, data) => {
    return new Promise((resolve, reject) => {
        fs.writeFile(file, data, error => {
            if (error) reject(error);
            resolve("file created successfully with handcrafted Promise!");
        });
    });
};

writeFilePromise(
        "/tmp/test2.js",
        "console.log('Hello world with handcrafted promise!');"
    )
    .then(result => console.log(result))

    .catch(error => console.log(error));
Enter fullscreen mode Exit fullscreen mode

With the arrival of Node.js V8, 'util.promisify()' alllowed us to convert I/O functions that return callbacks into I/O functions that return promises.

const fs = require("fs");
const util = require("util");

const writeFile = util.promisify(fs.writeFile);

writeFile("/tmp/test3.js", "console.log('Hello world with promisify!');")
  .then(() => console.log("file created successfully with promisify!"))

  .catch(error => console.log(error));
Enter fullscreen mode Exit fullscreen mode

Let's see the async/await version:

const fs = require("fs");
const { promisify } = require("util");

const writeFile = promisify(fs.writeFile);

async function main() {
    await writeFile("/tmp/test4.js",
        "console.log('Hello world with promisify and async/await!');");

    console.info("file created successfully with promisify and async/await!");
}

main().catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

In its latest release (Node.js V10), functions of the fs can return promises directly, eliminating the extra step and overhead of the old way. This due to its fs/promises API.

const fsp = require("fs/promises");

try {
    await fsp.writeFile("/tmp/test5.js", "console.log('Hello world with Node.js v10 fs/promises!'");
    console.info("File created successfully with Node.js v10 fs/promises!");
} catch (error){
    console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

Note I am using top level await (my await code is not inside and async function). This feature is experimental yet, so if you want to check it out, use the following flag: -- experimental-repl-await.

The fs/promises API provides the following methods:

access, copyFile, open, read, write, rename, truncate, ftruncate, rmdir, fdatasync, fsync, mkdir, readdir, readlink, symlink, fstat, lstat, stat, link, unlink, fchmod, chmod, lchmod, lchown, fchown, chown, utimes, futimes, realpath, mkdtemp, writeFile, appendFile and readFile.

Update:

In Node 13 it works even with ESM modules:

import { promises as fs } from 'fs';

try {
    await fs.writeFile("/tmp/test6.js", "console.log('Hello world with Node.js v13 fs.promises!'");
    console.info("File created successfully with Node.js v13 fs.promises!");
} catch (error){
    console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
adamshurwitz profile image
AdamHurwitz

It looks like "fs.writeFile(tempFile, response.auioContent, 'binary')" might return a promise now without using an additional library...

Some comments may only be visible to logged-in visitors. Sign in to view all comments.