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:
await operator.then(), .catch(), .finally()) or a Promise.* static method call (e.g.
all, resolve, try)new Promise(…) expressionsetTimeout(), setInterval(), setImmediate(), or requestAnimationFrame(), including
through window.this.$nextTick(), Vue.nextTick(), or nextTick() imported from vueasync functionTo 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.
<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>
<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>
<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>
<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>