Considering the 'no-return-await’ rule…

Yoichi Michael Nagano
3 min readMay 13, 2021

While completing a lesson exercise on React ToolKit’s createAsyncThunk(), I was was given instructions to compose the async callback function it takes as a second argument. This is an approximation of the code:

async () => {
const response = await fetchUsers();
return await response.json();
}

The role of this callback function is to provide the payload property of an action to dispatch to the Redux store to affect state. In this case, I was retrieving a list of users to then pass on as the payload. fetchUsers() fetches the data from an API; the data is then converted to an JavaScript object by json() before being returned note.

After writing this function, the IDE I was using threw this error: “Using return and await together is inefficient and should be avoided. You should split your await and return statements up instead.”

I rewrote the the function as instructed, returning a variable instead of the await statement:

async () => {
const response = await fetchUsers();
const users = await response.json();
return users;
}

That satisfied the testing framework, but I was curious: why was returning an await statement appreciably different in efficiency than returning a variable assigned to the return value of that same await statement?

To cut to the chase: there is not an appreciable difference. The source of this directive is likely the documentation for ESLint, which happens to have a no-return-await rule; it will throw an error if await occurs in a return statement in your code. You can work around this rule, as I did above, by splitting return and await into separate statements. However, doing so will not appreciably increase efficiency.

In fact, if efficiency truly is a concern when working with an async function, the above examples should do away with the second await statement altogether:

async () => {
const response = await fetchUsers();
return response.json();
}

Given that async functions always return a promise, the second await statement is essentially a redundancy. If the value returned by the second await statement will be wrapped in a promise anyway, a step is added by await-ing the return value of response.json() instead of simply returning the expression itself (since .json() is an async function, it returns a promise). This is an very helpful demonstration of the added work of returning an await statement.

This is essentially what the ESLint rule is attempting to discourage. But it is important to note here all the above variations of the same async function will work properly; they will all return a promise that resolves to list of users object. And, if performance isn’t a primary concern (with this use case, it probably shouldn’t be) there is a benefit to returning an await statement. By doing so, it is added to the stack trace, potentially saving some headache if this is part of a large project and needs debugging. In effect, this is a good defensive measure, with a likely very minor efficiency tradeoff.

To sum up: for most practical purposes it is likely preferable you ignore ESLint and return that await statement.

References

--

--

Yoichi Michael Nagano

Full Stack Software Engineer, film camera lover, Brooklyn resident.