235

I would like to use async/await with some filesystem operations. Normally async/await works fine because I use babel-plugin-syntax-async-functions.

But with this code I run into the if case where names is undefined:

import fs from 'fs';

async function myF() {
  let names;
  try {
    names = await fs.readdir('path/to/dir');
  } catch (e) {
    console.log('e', e);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

When I rebuild the code into the callback hell version everything is OK and I get the filenames. Thanks for your hints.

12 Answers 12

251

Native support for async/await fs functions since Node 11

Since Node.js 11.0.0 (stable), and 10.0.0 (experimental), you can access file system methods already promisify'd. Thanks to promises you can simply use try catch to handle exceptions instead of checking if the callback's returned value contains an error.

The API is very clean and elegant! Simply import file system methods from fs/promises instead of importing them directly from fs:

import fs from 'fs/promises'

async function listDir() {
  try {
    return await fs.readdir('path/to/dir')
  } catch (err) {
    console.error('Error occurred while reading directory:', err)
  }
}

listDir()
Sign up to request clarification or add additional context in comments.

5 Comments

This API is stable as of version 11.x per the File System documentation on the Node.js site
@DanStarns if you don't return await your promise, the catch block is of no use...I thinj it is sometimes a good practice to await before returning
Documentation for these alternative methods: nodejs.org/api/fs.html#fs_fs_promises_api
i like doing import { promises as fs } from 'fs';
Suggestion: import fs from 'fs/promises;` and then await fs.readdir(...)
180

Starting with node 8.0.0, you can use this:

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

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

async function myF() {
  let names;
  try {
    names = await readdir('path/to/dir');
  } catch (err) {
    console.log(err);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

See https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original

8 Comments

In node v8.9.4, got a SyntaxError: Unexpected token import error message. does node8 supports import token by default?
@makerj he's using the new import syntax. It currently requires some transpiling. Would be ok to also use const fs = require('fs') or const { promisify } = require('util')
Noob question, but what's the {err, names} = function syntax called?
@Qasim it is called destructuring assignment.
@AlexanderZeitler That may be true. I haven't looked to see if that is actually a correct use of destructuring. In the case of async await I think you would just do names = await readdir('path/to/dir'); and if there is an err handle it in the catch block. Either way, the name of the syntax is destructuring assignment which was just in response to Qasim's question.
|
110

Node.js 8.0.0

Native async / await

Promisify

From this version, you can use native Node.js function from util library.

const fs = require('fs')
const { promisify } = require('util')

const readFileAsync = promisify(fs.readFile)
const writeFileAsync = promisify(fs.writeFile)

const run = async () => {
  const res = await readFileAsync('./data.json')
  console.log(res)
}

run()


Promise Wrapping

const fs = require('fs')

const readFile = (path, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.readFile(path, opts, (err, data) => {
      if (err) reject(err)
      else resolve(data)
    })
  })

const writeFile = (path, data, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.writeFile(path, data, opts, (err) => {
      if (err) reject(err)
      else resolve()
    })
  })

module.exports = {
  readFile,
  writeFile
}

...


// in some file, with imported functions above
// in async block
const run = async () => {
  const res = await readFile('./data.json')
  console.log(res)
}

run()

Advice

Always use try..catch for await blocks, if you don't want to rethrow exception upper.

5 Comments

This is strange. I am getting SyntaxError: await is only valid in async function... crying in rage.
@VedranMaricevic. look at comments, await must be always in async block :)
@VedranMaricevic. You need to call that const res = await readFile('data.json') console.log(res) in some async function
promise wrapping fs.promises and using it with async/await is so confusing to me
@PrimitiveNom Promise can be used in traditional way within then, catch etc. Where are async/await is modern behavior flow.
77
+400

As of v10.0, you can use fs.Promises

Example using readdir

const { promises: fs } = require("fs");

async function myF() {
    let names;
    try {
        names = await fs.readdir("path/to/dir");
    } catch (e) {
        console.log("e", e);
    }
    if (names === undefined) {
        console.log("undefined");
    } else {
        console.log("First Name", names[0]);
    }
}

myF();

Example using readFile

const { promises: fs } = require("fs");

async function getContent(filePath, encoding = "utf-8") {
    if (!filePath) {
        throw new Error("filePath required");
    }

    return fs.readFile(filePath, { encoding });
}

(async () => {
    const content = await getContent("./package.json");

    console.log(content);
})();

8 Comments

Works great, but important to note the open issue regarding the ExperimentalWarning: The fs.promises API is experimental warning: github.com/pnpm/pnpm/issues/1178
@DavidP what version of node are you using? 12 and above works fine
Yes! Absolutely correct - I neglected to state version I am on: v10.15.3 - it's possible to suppress the message. However, with the issue still open I thought it worth mentioning.
@DavidP I mean it is worth a mention don't get me wrong, but node 12 is in LTS now so it's not a Biggie.
In TypeScript (and modern JavaScript?) you can write import { promises as fs } from "fs";.
|
49

You might produce the wrong behavior because the File-Api fs.readdir does not return a promise. It only takes a callback. If you want to go with the async-await syntax you could 'promisify' the function like this:

function readdirAsync(path) {
  return new Promise(function (resolve, reject) {
    fs.readdir(path, function (error, result) {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

and call it instead:

names = await readdirAsync('path/to/dir');

2 Comments

I'm getting a weird response like this... Buffer(18524) [60, 115, 99, 114, 105, 112, 116, 32, 116, 110, 116, 45, 108, 105, 98, 62, 13, 10, 32, 32, 32, 32, 47, 42, 42, 13, 10, 32, 32, 32, 32, 32, 42, 32, 67, 111, 112, 121, 114, 105, 103, 104, 116, 32, 63, 32, 50, 48, 50, 48, 32, 68, 101, 115, 105, 103, 110, 32, 65 …]
Use Buffer.toString method.
22

Node v14.0.0 and above

you can just do:

import { readdir } from "fs/promises";

just like you would import from "fs"

see this PR for more details: https://github.com/nodejs/node/pull/31553

2 Comments

This should be the selected answer as of 2022.
I like how as I scroll down I get to see the history of file system access in Node.js. xD
20

This is the TypeScript version to the question. It is usable after Node 11.0:

import { promises as fs } from 'fs';

async function loadMonoCounter() {
    const data = await fs.readFile('monolitic.txt', 'binary');
    return Buffer.from(data);
}

Comments

8
+200

I have this little helping module that exports promisified versions of fs functions

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

module.exports = {
  readdir: promisify(fs.readdir),
  readFile: promisify(fs.readFile),
  writeFile: promisify(fs.writeFile)
  // etc...
};

Comments

5

Here is what worked for me:

const fsp = require('fs-promise');

(async () => {
  try {
    const names = await fsp.readdir('path/to/dir');
    console.log(names[0]);
  } catch (e) {
    console.log('error: ', e);
  }
})();

This code works in node 7.6 without babel when harmony flag is enabled: node --harmony my-script.js. And starting with node 7.7, you don't even need this flag!

The fsp library included in the beginning is just a promisified wrapper for fs (and fs-ext).

I’m really exited about what you can do in node without babel these days! Native async/await make writing code such a pleasure!

UPDATE 2017-06: fs-promise module was deprecated. Use fs-extra instead with the same API.

1 Comment

Downloading a library for this is pure overkill, dependency bloating is something that the community should be strongly against, infact a new npmjs should come into making that only has libs with 0 dependencies
5

Recommend using an npm package such as https://github.com/davetemplin/async-file, as compared to custom functions. For example:

import * as fs from 'async-file';

await fs.rename('/tmp/hello', '/tmp/world');
await fs.appendFile('message.txt', 'data to append');
await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);

var stats = await fs.stat('/tmp/hello', '/tmp/world');

Other answers are outdated

Comments

3

you can use this code :

fs.promises.readdir(path)

// If promise resolved and
// data are fetched
.then(filenames => {
    for (let filename of filenames) {
        console.log(filename)
    }
})

// If promise is rejected
.catch(err => {
    console.log(err)
})

Comments

0

You can use the simple and lightweight module https://github.com/nacholibre/nwc-l it supports both async and sync methods.

Note: this module was created by me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.