Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to use async/await almost everywhere?

I'm currently writing small NodeJS CLI tool for personal usage and I've decided to try ES7 async/await feature with Babel.

It's a network tool so I obviously have asynchronous network requests. I wrote a simple wrapper for request package:

export default function(options) {
    return new Promise(function(resolve, reject) {
        request({...options,
            followAllRedirects: true,
            headers: {
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0"
            }
        }, (error, response, body) => {
            if(error) {
                return reject(error);
            }
            resolve({response: response, body: body});
        });
    });
}

Now I can do something like

async function getGooglePage() {
    try {
        var r = await request({url: "http://google.com"});

        console.log(r.body);
        console.log("This will be printed in the end.")
    } catch(e) {
        console.log(e);
    }
}
getGooglePage();

And now I have a question: I do requests in many places and I have to mark all these functions as async, is it a good practice? I mean that almost every function in my code should be async because I need to await a result from other async functions. That's why I think that I misunderstood async/await concept.

like image 340
user0103 Avatar asked Feb 13 '16 12:02

user0103


Video Answer


3 Answers

async/await is sometimes called "contagious" or "viral" (or so it has in the C# world), because in order for it to be effective, it needs to be supported all the way down the call chain. Forcing something asynchronous to act synchronous can lead to unintended results, so you should extend it from the original method all the way down to the top level consumer using it. In other words, if you create or use a type that uses it, that type should also implement it, and so on all the way up the chain. So yes, it's expected that you add async to every function that itself relies on it. Just note, however, you should not add preemptively add async to functions that don't actually implement or need it.

Just think: If you use async (by awaiting something, I mean), you are async. Avoid squashing an async call into something synchronous.

like image 181
moribvndvs Avatar answered Oct 23 '22 07:10

moribvndvs


I do requests in many places and I have to mark all these functions as async

Yes, if all your code is asynchronous, then you'd use async functions everywhere.

Having all your code be asynchronous makes things complicated though. You have to worry about race conditions everywhere, make sure to handle reentrant functions correctly, and remember that during every await basically anything can happen.

I mean that almost every function in my code should be async because I need to await a result from other async functions.

This might not be a best practise. You could try to break down your code into smaller units, most of which are usually not asynchronous. So instead of writing

async function getXandThenDoY(xargs) {
    let res = await get(xargs);
    …
    return …;
}

you should consider making two functions

function doY(res) {
    // synchronous
    …
    return …;
}
function getXandDoY(xargs) {
    // asynchronous
    return get(xargs).then(doY);
}
/* or, if you prefer:
async function getXandDoY(xargs) {
    return doY(await get(xargs));
}
*/
like image 42
Bergi Avatar answered Oct 23 '22 08:10

Bergi


I had the same question.

And found a great answer here → ‘Pitfall 3: your whole stack needs to be async’

No, async/await is not contagious. (I believed the same thing for some time, too)

You can always treat the result of a sync function like a promise, and you are back to normal.

From developer.mozilla.org:

The async function declaration defines an asynchronous function…

Return value: A Promise which will be resolved with the value returned by the async function, or rejected with an uncaught exception thrown from within the async function.

Sample code:

const log = console.log; // just lazy shorthand

// just to delay, as seen in many places
function promiseTimeout(time, value) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() { resolve(value); }, time);
    });
};

// the thing you care about
async function foo() {
    Promise.resolve('here')
    .then((a) => {log('doing stuff '+a); return 'pear'})
    .then(function(v) {
        return promiseTimeout(1000,v)
    });
};

// treat async-function like promise:
foo().then(function(){ log('folling up in main, ')});
// log('bad end');

gets you:

doing stuff here
following up in main

Enabling 'bad end' would show up too early. You can only await stuff it you use await. (And if you do, remember: It's just syntactic sugar, saving you of stuffing your follow-up code into .then() clasuses... nice, but no more than that.)

like image 2
Frank Nocke Avatar answered Oct 23 '22 08:10

Frank Nocke