Skip to content

Commit 9ff875c

Browse files
committed
fix(vue-3): resolve race conditions
1 parent e049174 commit 9ff875c

File tree

1 file changed

+49
-37
lines changed

1 file changed

+49
-37
lines changed

vue-3/App.vue

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,63 @@
11
<script setup lang="ts">
2-
import { onWatcherCleanup, ref, reactive, watchEffect } from "vue";
2+
import type { Post } from "jsonplaceholder-types/types/post";
3+
import type { User } from "jsonplaceholder-types/types/user";
4+
import { onWatcherCleanup, reactive, ref, watchEffect } from "vue";
35
46
const urlBase = "https://jsonplaceholder.typicode.com";
57
6-
const selectedUserId = ref();
7-
const users = reactive({ value: undefined, loading: false });
8-
const posts = reactive({ value: undefined, loading: false });
8+
const selectedUserId = ref<number>();
9+
const posts = reactive<{
10+
value?: Post[];
11+
loading: boolean;
12+
}>({ loading: false });
13+
const users = reactive<{
14+
value?: User[];
15+
loading: boolean;
16+
}>({ loading: false });
917
10-
watchEffect(function fetchUsers() {
18+
async function fetchUsers(signal: AbortSignal) {
1119
users.loading = true;
12-
const controller = new AbortController();
13-
onWatcherCleanup(() => controller.abort());
20+
try {
21+
const response = await fetch(`${urlBase}/users`, { signal });
22+
const data = await response.json();
23+
signal.throwIfAborted();
24+
users.value = data;
25+
users.loading = false;
26+
} catch (error) {
27+
if (signal.aborted) return;
28+
users.loading = false;
29+
throw error;
30+
}
31+
}
1432
15-
fetch(`${urlBase}/users`, { signal: controller.signal })
16-
.then(async (response) => {
17-
users.value = await response.json();
18-
})
19-
.catch((error) => {
20-
if (!(error instanceof DOMException && error.name === "AbortError")) {
21-
throw error;
22-
}
23-
})
24-
.finally(() => {
25-
users.loading = false;
33+
async function fetchPosts(userId: number, signal: AbortSignal) {
34+
posts.loading = true;
35+
try {
36+
const response = await fetch(`${urlBase}/posts?userId=${userId}`, {
37+
signal,
2638
});
39+
const data = await response.json();
40+
signal.throwIfAborted();
41+
posts.value = data;
42+
posts.loading = false;
43+
} catch (error) {
44+
if (signal.aborted) return;
45+
posts.loading = false;
46+
throw error;
47+
}
48+
}
49+
50+
watchEffect(function updateUsers() {
51+
const ctrl = new AbortController();
52+
onWatcherCleanup(() => ctrl.abort());
53+
fetchUsers(ctrl.signal);
2754
});
2855
29-
watchEffect(function fetchPosts() {
56+
watchEffect(function updatePosts() {
3057
if (selectedUserId.value === undefined) return;
31-
posts.loading = true;
32-
const controller = new AbortController();
33-
onWatcherCleanup(() => controller.abort());
34-
35-
fetch(`${urlBase}/posts?userId=${selectedUserId.value}`, {
36-
signal: controller.signal,
37-
})
38-
.then(async (response) => {
39-
posts.value = await response.json();
40-
})
41-
.catch((error) => {
42-
if (!(error instanceof DOMException && error.name === "AbortError")) {
43-
throw error;
44-
}
45-
})
46-
.finally(() => {
47-
posts.loading = false;
48-
});
58+
const ctrl = new AbortController();
59+
onWatcherCleanup(() => ctrl.abort());
60+
fetchPosts(selectedUserId.value, ctrl.signal);
4961
});
5062
</script>
5163

0 commit comments

Comments
 (0)