October 4, 2022

Reading Files With Node.js

Reading Files With Node.js

Today we are going to look at 4 different ways you can load data from a file in Node.js using only built in functions. These will cover the majority of cases you will encounter in real life.

  1. Asynchronously using Callbacks
  2. Asynchronously using Promises
  3. Asynchronously using Async/Await
  4. Synchronously

Each of these methods will return control to your application when the whole file has been loaded into memory. This could be problematic if the file is very big. For very big files check out our article on reading from streams.

For each example below we will use a file named HelloWorld.txt as our input with the following text inside it:

Hello World

Asynchronously using Async/Await

This is the form you are probably most likely to come across in more modern applications due to the popularity of Async/Await. Β The structure of the example changes a little bit to a include a load data function, because one of the limitations of Async/Await is that 'await' can only be used inside a function marked 'async'.

import { promises as fs } from 'fs';

async function loadData() {
    try {
        const data = await fs.readFile('./HelloWorld.txt')
        console.log('Content:', data.toString());
        console.log('Finished reading file');
    catch(err) {
        console.error('Error reading file data', err);
    }
}

console.log('Reading file data');
loadData()
console.log('Running other functions...')
Reading file data
Running other functions...
content: Hello World
Finished reading file

Asynchronously using Promises

This time we use the fs.promises api which gives a us a slightly different interface to the file system api. The fs.promises api allows us to access the file system through a promise chain making the code look a bit more modern than the callback example.

import { promises as fs } from 'fs';

console.log('Reading file data');
fs.readFile('./HelloWorld.txt').then((data) => {
    console.log('Content:', data.toString());
    console.log('Finished reading file');
}).catch((err) => {
    console.error('Error reading file data', err);
});

console.log('Running other functions...')
Reading file data
Running other functions...
content: Hello World
Finished reading file

The output is the same as before.

Asynchronously using callbacks

The asynchronous callback method is the oldest and most idomatic way of loading files. Promises and Async/Await have made code much easier to read so you are less likely to see this in code.

import fs from 'fs';

console.log('Reading file data');
fs.readFile('./HelloWorld.txt', (err, data) => {
    if (err) {
      console.error('Error reading file data', err);
      return
    }
    console.log('Content:', data.toString());
    console.log('Finished reading file');
});

console.log('Running other functions...')
Reading file data
Running other functions...
content: Hello World
Finished reading file

Again, the output is the same as before.

Synchronously

This is probably the mode you should see the least. Unless you are writing very small one off scripts or CLI tools, synchronous file reading may be an idicator of code smell. This is because the synchronous method will block execution of your program until the file has finished being read. In a multi user application like a web server, this can cause performance issues

import fs from 'fs';

console.log('Reading file data');

const data =  fs.readFileSync('./HelloWorld.txt');
console.log('content', data.toString());
console.log('Finished reading file');

console.log('Running other functions...')
Reading file data
content: Hello World
Finished reading file
Running other functions...

Notice that in this example, 'Running other functions...' comes after 'Finished reading file'.

Which method should you use

Use Async/Await for modern javascript or use Promises if you are working on an existing code base tha hasn't switched over to Async/Await.
Async/Await is the most readable form that won't block the application.
Promises are slightly less readable than Async/Await but also won't block the application.

Readable Non-Blocking
Asynchronously using Async/Await 🟒 🟒
Asynchronously using Promises 🟑 🟒
Asynchronously using Callbacks 🟑 🟒
Synchronously 🟒 πŸ”΄

If you are writing a shell script or quickly testing something, you may want to use the synchronous method, but here are very few reasons for it to appear in production code.