Vuex-module-decorators actions are async

June 11th 2021 Vuex

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.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License