15

I am learning about Javascript Promise and async/await. The sample code below asynchronously reads and parses a JSON file in node.js (my node.js version is v10.0.0).

In the sample code, ChainReadJson function and AwaitReadJson function are doing the same thing, reading and parsing a JSON file. The difference is that ChainReadJson function uses a promise chain, while AwaitReadJson function uses async/await.

const FS = require("fs");

function ReadFile(fileName) {
    return new Promise((Resolve, Reject) => {
        FS.readFile(fileName, 'utf8', (error, result) => {
            if (error)
                Reject(error);
            else
                Resolve(result);
        });
    });
}

// function using promise chain

function ChainReadJson(fileName, CallBack) {
    ReadFile(fileName)
        .then(
            res => JSON.parse(res),
            err => {
                Message(-1, err.message);
            }
        )
        .then(
            res => {
                if (res !== undefined)
                    CallBack(fileName, res);
            },
            err => {
                Message(-2, err.message);
            }
        );
}

// function using async/await

async function AwaitReadJson(fileName, CallBack) {
    let res, json;

    try {
        res = await ReadFile(fileName);
    }
    catch (err) {
        Message(-1, err.message);
        return;
    }
    try {
        json = JSON.parse(res);
    }
    catch (err) {
        Message(-2, err.message);
        return;
    }
    CallBack(fileName, json);
}

ChainReadJson('test.json', PrintJSON);
AwaitReadJson('test.json', PrintJSON);

// common functions

function PrintJSON(fileName, json) {
    console.log(`JSON[${fileName}]:`, json);
}

function Message(n, str) {
    console.log(`[${n}]`, str);
}

When writing the code for ChainReadJson function using promise chain, I had difficulties controlling execution results and errors. However, when writing the code for AwaitReadJson function using async/await, those difficulties are mostly disappeared.

Do I correctly understand the benefits of async/await? What are the disadvantages of async/await compared to promise chain?

(The sample code is a modified version of the code in this answer. The original code uses promise chain only, and is written to know exactly where in the chain the error occurred and what is the error)

pdh0710
  • 407
  • 1
  • 5
  • 14
  • 1
    Uou understand both promise chains and async/await well . . . and the rest is opinion which this site isn't good at. Ignoring that in my humble opinion there aren't really any disadvantages to async/await aside from it not being supported everywhere. – generalhenry May 07 '18 at 22:29
  • What errors did you get with using promise chain? And how did you use ChainedPromiseJSON? The 2 codes are not the same, in the promise chain you only execute Callback if the return from `JSON.parse` was not `undefined`, whereas in your await case, you execute Callback always. – cowbert May 07 '18 at 22:35
  • @generalhenry : Thank you for opinion. – pdh0710 May 07 '18 at 22:39
  • @cowbert : The sample code is correctly working. If you read [this answer](https://stackoverflow.com/a/50210073/7339376), you can understand the sample code more easily. – pdh0710 May 07 '18 at 22:41
  • why did you create 2 separate questions. Anyway, if the previous step (the first `then` caused an error, then it will hit the error callback in the second `then`). Checking for `res !== undefined` will only get executed if `JSON.parse` returns undefined, which won't ever happen, it will throw a SyntaxError instead. – cowbert May 07 '18 at 22:48
  • @cowbert : Previous question is not for async/await, and this sample code is correctly working in my node.js(v10.0.0). It seems that you still do not understand [this answer](https://stackoverflow.com/a/50210073/7339376). – pdh0710 May 07 '18 at 23:33

2 Answers2

13

Indeed, async/await were designed to reduce boilerplate and make asynchronous programs easier to write, compared to callbacks, promises, and generator functions.

  • While promises were created with the same goal, they had the additional constraint of having to work in the existing JS engines -- so their syntax is more complicated. Using async/await requires a relatively new JS engine. It might not matter if you're writing a node.js app of your own, but a library might need to be compatible with older node.js versions (and I'm not sure if you can transpile it for use in older browsers without generator support).
  • Since async/await is newer, it's not as optimized. A comparison made in the last year reports Bluebird promises (a JS library implementing simplified version of promises) outperforming async/await in a certain benchmark. (Of course this may not matter when your use-case is making a few network requests.)
  • You might still need promises to execute several asynchronous actions in parallel (edit: if you need their results)
Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • Thank you for your answer and correcting my question. It is a great help to me. Also I agree with you that this is a disadvantage of async/await : "Using async/await requires a relatively new JS engine" – pdh0710 May 07 '18 at 23:20
  • About this : "still need promises to execute several asynchronous actions in parallel" ... When I execute several async/await functions simultaneously, they just work in parallel. I think this is a natural consequence, because async/await function operates on Promise base. Am I right? – pdh0710 May 07 '18 at 23:52
  • @pdh0710 If by "execute" you mean calling the async function without `await`, then yes. But since you usually care about the results of the async operation (if only for error handling), you'll have to deal with promises returned. – Nickolay May 07 '18 at 23:59
  • I executed several async functions with await. By the way, async function without await is useful? – pdh0710 May 08 '18 at 00:25
  • @pdh0710 If you mean `await f1();await f2();` then no, they are _not_ run in parallel, [check out this example from MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#Examples), it also demonstrates why you would call an async function without `await`. I have to go now, so if you have further questions, try posting another question or maybe find a real-time chat -- stackoverflow comments are a poor communication tool. – Nickolay May 08 '18 at 00:43
4

While async/await can be a nice way to cleanup asynchronous logic, it's worth pointing out that the promise logic can be cleaned up significantly, to the point of being very similar to the async/await alternative:

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

//Could also use the experimental "fs/promise" api from node v10
const promisifiedReadFile = util.promisify(fs.readFile);

const readFile = (fileName) => promisifiedReadFile(fileName, 'utf8');

function chainReadJson(fileName, callback) {
    return readFile(fileName)
        .then(json => JSON.parse(json))
        .then(result => callback(null, result))
        .catch(e => {
            console.log("Error reading or parsing file", e.message);
            callback(e)
        });
}

The only functional difference here is that all the error logging occurs at one place, at the end of the chain.

It's possible to preserve the split logging for the readFile and the JSON.parse, but that is admittedly a bit trickier. You generally want to re-throw errors after handling them, so that the downstream .then handlers are skipped: but if you throw the error again, it'll be caught again by the downstream .catch handlers, which will cause duplicate logging, if you don't find a way to filter it out.

It's doable, but it's a bit of a pain, so I left it out of the above code.

Retsam
  • 30,909
  • 11
  • 68
  • 90