Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.

For example, if I have this function:

async function func(x) {
    ...
    return y;
}

Then I always call it as follows:

async function func2(x) {
    let y = await func(x);
    ...
}

I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.

I have concluded that it is "legitimate" to drop the await only directly within a return statement.

For example:

async function func2(x) {
    ...
    return func(x); // instead of return await func(x);
}

Is this conclusion correct, or else, what am I missing here?

EDIT:

A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:

It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.

For example, removing the await in the code below is "dangerous":

async function func1() {
    try {
        return await func2();
    }
    catch (error) {
        return something_else;
    }
}

The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.

like image 815
goodvibration Avatar asked Nov 21 '17 08:11

goodvibration


People also ask

What happens if you dont use await?

If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call.

What happens if you don't await a promise?

Otherwise, if you don't use it, nothing good or bad will happen by default - the code inside the promise will still run, but nothing will happen when you call resolve . If you call reject , however, then nothing will handle the rejection; which is bad, as it throws an error on most platforms.

How do you use await correctly?

async and awaitInside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

Why do we need await?

The await operator is used to wait for a Promise and get its fulfillment value. It can only be used inside an async function or a JavaScript module.


3 Answers

An async function always returns a Promise.

So please keep in mind that these writing of an async function are all the same:

// tedious, sometimes necessary
async function foo() {
    return new Promise((resolve) => resolve(1)))
}

// shorter
async function foo() {
    return Promise.resolve(1)
}

// very concise but calling `foo` still returns a promise
async function foo() {
    return 1 // yes this is still a promise
}

You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.

As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.

Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.

Compare these two examples within the scope of an async function:

async function func() {
    const foo = await tediousLongProcess("foo") // wait until promise is resolved
    const bar = await tediousLongProcess("bar") // wait until promise is resolved

    return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}

with:

async function func() {
     promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
     return Promise.all(promises) // returns a promise on success you have its values in order
}

The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.

In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.

(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)

I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.

like image 151
k0pernikus Avatar answered Oct 23 '22 19:10

k0pernikus


If func is an async function then calling it with and without await has different effects.

async function func(x) {
  return x;
}

let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)

It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).

func(1).then(z => /* use z here */)

If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.

like image 31
Dan Prince Avatar answered Oct 23 '22 19:10

Dan Prince


await just lets you to treat promises as values, when used inside an async function.

On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).

So:

  • I have concluded that it is "legitimate" to drop the await only directly within a return statement.

In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.

So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.

So yes, is always safe to drop await in the last return statement.

PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.

PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.

like image 2
Sergeon Avatar answered Oct 23 '22 20:10

Sergeon