Vuex-module-decorators actions are async
The vuex-module-decorators package makes it easy to create a strongly typed Vuex store for use with TypeScript. However, there are some pitfalls in handling errors in actions.
First, the rawError
parameter in the @Action
decorator must be set so that the errors thrown are not wrapped, making it impossible to inspect and properly handle them in the calling code:
@Action({ rawError: true })
async throwsAsync(): Promise<void> {
throw new Error('From async action.')
}
The calling code can now handle the errors in the usual way, using a try-catch
block:
try {
await getSampleModule(this.$store).throwsAsync();
} catch (err) {
// inspect and handle error
}
However, this does not work with a synchronous action like the following:
@Action({ rawError: true })
throwsSync(): void {
throw new Error('From sync action.')
}
The calling code can still use a try-catch
block:
try {
getSampleModule(this.$store).throwsSync();
} catch (err) {
// inspect and handle error
}
But it would not catch the error as seen in the browser console:
Uncaught (in promise) Error: From sync action.
Although the action is synchronous, there still appears to be a promise involved. This can be explained by the following quote from the documentation:
If you are doing a long running task inside your action, it is recommended to define it as an async function. But even if you do not, this library will wrap your function into a Promise and await it.
This is why the code that calls the method synchronously does not catch the error. The method does not throw an error. Instead, it returns a rejected promise. To catch the error, you should treat the method as asynchronous:
try {
await getSampleModule(this.$store).throwsSync();
} catch (err) {
// inspect and handle error
}
This works, but it is error-prone because the method's signature is misleading. It does not indicate that it should be called asynchronously due to the transformation by the vuex-module-decorators package.
To avoid this situation, it is best to make all actions in the store asynchronous, even if you do not call any asynchronous or long-running methods in it.
A sample project demonstrating this behavior can be found in my GitHub repository.
The vuex-module-decorators package wraps synchronous actions in a promise. This means that their signature is not correct. To avoid this problem, it's best to make all your actions asynchronous, even if you don't need to.