Considering the 'no-return-await’
rule…
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
- ESLint Documentation
- Why Using ‘return await’ is a bad idea?
- MDN Web Docs on async functions
- Thanks also to Chris Maddern and Josh Goldberg