1
\$\begingroup\$

Follow up to Read files from a directory using Promises

Removed exists() and replaced with stat() so I can check if the file is readable before proceeding.

console.log("Hi");
const fs = require('fs');
const { promisify } = require('util');

function mystat(file) {
    return new Promise(function(resolve, reject) {
        console.log(file);
        fs.stat(file, function(err, stat) {
            if (err) {
                reject(err);
            }
            else {
                console.log("Returning Obj");
                resolve({stat: stat, file: file});
            }
        });
    });
}



readdir     = promisify(fs.readdir);
readFile    = promisify(fs.readFile);
writeFile   = promisify(fs.writeFile);

readdir(".")
.then(files => mystat(files[0]))
.then(data  => {if (!(data.stat.mode | fs.S_IRWXO)) { throw "Not Readable";} return readFile(data.file)})
.then(content => writeFile('Test', content))
.then(() => console.log("It worked"))
.catch(err => console.log("Error: " + err));

To pass parameters through, eg the file passed through stat to the read function; I needed to define my own function so that I could pass the object on). Is there a better way of doing this?

\$\endgroup\$
3
\$\begingroup\$

First of all, async code is not a requirement for all Node.js code. It depends on the use case. Async code makes sense if you want to service multiple things concurrently (i.e. HTTP servers, test runners, etc.) but doesn't make sense for single-run script-y things (i.e. cli tool, scaffold generator, etc.).

The case above falls in the second category, a simple, single-run, non-concurrent, non-intensive operation. This means you can get away with the synchronous versions of the same operations, which will not require promises.

const { readdirSync, statSync, readFileSync, writeFileSync } = require('fs');

readdirSync('.')
  .slice(0, 1)
  .map(file => ({ stat: statSync(f), file }))
  // If you know it's not readable, just don't proceed.
  .filter(data => (data.stat.mode | fs.S_IRWXO))
  .map(data => readFileSync(data.file))
  .forEach(content => writeFileSync('test', content))

// If it made it all the way here, it worked. If something threw up,
// it should have halted the script somewhere in the middle.
console.log("It worked")

Now if you really want async code, Node supports async/await. It's essentially syntactic sugar to promises, where async functions return promises, values returned by async functions become the resolved value, errors throw become rejection values, and await makes code appear to wait for the promise to settle before proceeding. This means you can write the code much closer to the one above.

The one nice thing about async/await over regular promises is better scoping. You don't have to pass forward stuff from the previous then to the next then.

const { promisify } = require('util')
const fs = require('fs')
const readdir = promisify(fs.readdir)
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
const stat = promisify(fs.stat)

async function op(){
  const files = await readdir('.')
  const file = files[0]
  const data = await stat(file)

  if (!(data.stat.mode | fs.S_IRWXO)) throw "Not Readable"

  const content = await readFile(file)

  await writeFile('Test', content)
}

// Needed, since Node will warn you about unhandled rejections.
// Otherwise, the code above could have been an async iife.
op().then(() => {
  console.log("It worked")
}, (e) => {
  console.log("Error: " + e)
})
\$\endgroup\$
2
  • \$\begingroup\$ This is a very simple example (that does something slightly useful). But what I am trying to learn the best way to use async code. Does await block the thread or simply provide a way to make the code look sequential or does it still allow truly async behavior? \$\endgroup\$ Mar 23 '18 at 21:50
  • \$\begingroup\$ @MartinYork await doesn't block the thread, it just makes it look like it does. It's still the same machinery as promises - it doesn't run the next then unless the previous one settles. A good way to transition from promsies to async/await is to think of it as promises and then, except without the then around your code. \$\endgroup\$
    – Joseph
    Mar 24 '18 at 0:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.