Why is this an issue?

A computed property’s getter must run synchronously for Vue’s reactivity system to track which reactive dependencies it reads. That tracking only stays active while the getter is running synchronously: it closes as soon as the getter suspends at an await or hands off to a .then()/.catch()/.finally() callback, a timer callback, or a nextTick() callback. Dependencies read before that point are tracked normally; any dependency read after it is never registered, so a later change to that specific dependency will not trigger a re-evaluation and the cached value can silently go stale.

This rule raises an issue when a computed property’s getter, or the function passed to computed(), contains:

To fix this, move the asynchronous logic out of the computed property and into a watch callback or a lifecycle hook that assigns its result to a piece of reactive state instead. If you specifically need a value that is derived asynchronously, consider the computedAsync composable from VueUse.

Code examples

Noncompliant code example

<script>
export default {
  data() {
    return { userId: 1 };
  },
  computed: {
    async userName() { // Noncompliant: the getter is declared "async"
      const response = await fetch(`/api/users/${this.userId}`); // Noncompliant: the continuation after "await" runs outside dependency tracking
      const data = await response.json(); // Noncompliant: the continuation after "await" runs outside dependency tracking
      return data.name;
    }
  }
}
</script>

Compliant solution

<script>
export default {
  data() {
    return { userId: 1, userName: null };
  },
  watch: {
    userId: {
      immediate: true,
      async handler(newUserId) {
        const response = await fetch(`/api/users/${newUserId}`);
        const data = await response.json();
        this.userName = data.name;
      }
    }
  }
}
</script>

Noncompliant code example

<script setup>
import { computed, ref } from 'vue';

const query = ref('');

const searchResults = computed(async () => { // Noncompliant: the getter is declared "async"
  const data = await searchItems(query.value); // Noncompliant: the continuation after "await" runs outside dependency tracking
  return data;
});

function searchItems(query) {
  return fetch(`/api/search?q=${query}`).then(response => response.json());
}
</script>

Compliant solution

<script setup>
import { computed, ref, watch } from 'vue';

const query = ref('');
const results = ref([]);

watch(query, async newQuery => {
  results.value = await searchItems(newQuery);
}, { immediate: true });

const searchResults = computed(() => results.value);

function searchItems(query) {
  return fetch(`/api/search?q=${query}`).then(response => response.json());
}
</script>

Resources

Documentation