Is it OK to use async/await almost everywhere?
Asked Answered
M

3

18

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.

Mcadams answered 13/2, 2016 at 12:58 Comment(0)
B
17

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.

Backdrop answered 13/2, 2016 at 13:32 Comment(3)
I don't think the viral part applies so much to JavaScript, in that it seems more of an implementation choice whether a promise-returning function is implemented with promises or as an async function. There are also very few types in JavaScript.Homopterous
@Homopterous In all fairness, things like the C# world just return a generic Task which is effectively used somewhat like a promise in this case, and async/await are just syntactic sugar. Again, I think mixing the two concepts is a good way to create intended results, but I understand what you mean in the sense that in the C# world, the viral nature is reinforced by the fact that the compiler requires it, but there's no such thing in JavaScript.Backdrop
I agree with @Homopterous There's nothing viral about async. There's virtually no difference between an async function vs. a normal function returning a promise. An async function can be processed just like a promise.Gapeworm
I
6

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));
}
*/
Impossible answered 13/2, 2016 at 13:36 Comment(0)
A
2

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.)

Account answered 17/4, 2018 at 6:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.