watch-代码片段
1.代码
记录下一段代码
javascript
<script setup>
import { ref, watch } from 'vue'
const id = ref(1)
let currentRequestId = 0
// 模拟异步请求
function fetchData(requestId, newId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: newId, data: `Data for ID ${newId}` })
}, 1000) // 模拟 1 秒的网络延迟
})
}
watch(id, async (newId, oldId) => {
console.log(`ID changed from ${oldId} to ${newId}`)
const requestId = ++currentRequestId
try {
const result = await fetchData(requestId, newId)
// 检查这个请求是否仍然是最新的
if (requestId === currentRequestId) {
console.log('Request completed with latest ID:', result)
} else {
console.log('Request completed, but ID has changed. Discarding result.')
}
} catch (error) {
console.error('Error fetching data:', error)
}
})
// 模拟 ID 变化
setTimeout(() => { id.value = 2 }, 500)
setTimeout(() => { id.value = 3 }, 1500)
</script>
<template>
<div>
<p>Current ID: {{ id }}</p>
</div>
</template>
这段代码记录下,
-
初始化阶段:
javascriptconst id = ref(1) let currentRequestId = 0
id
被初始化为响应式引用,值为1。currentRequestId
初始化为0。
-
设置 watch 函数:
javascriptwatch(id, async (newId, oldId) => { ... })
- Vue设置了一个观察器,监听
id
的变化。 - 此时,watch函数还未执行,只是被定义和注册。
- Vue设置了一个观察器,监听
-
设置定时器:
javascriptsetTimeout(() => { id.value = 2 }, 500) setTimeout(() => { id.value = 3 }, 1500)
- 两个定时器被添加到事件队列中。
-
主线程执行完毕,进入事件循环。
-
500ms后:
- 第一个定时器触发。
id.value
被设置为2。- 这触发了 watch 函数。
-
Watch 函数第一次执行:
currentRequestId
增加到1。fetchData(1, 2)
被调用,返回一个Promise。- 这个Promise被添加到微任务队列。
-
1000ms后(自步骤5开始):
fetchData
的定时器触发。- Promise 解析,结果添加到微任务队列。
-
1500ms后(自开始):
- 第二个定时器触发。
id.value
被设置为3。- 这再次触发了 watch 函数。
-
Watch 函数第二次执行:
currentRequestId
增加到2。fetchData(2, 3)
被调用,返回另一个Promise。
-
微任务队列处理:
- 第一个
fetchData
的结果被处理。 - 但是
requestId (1) !== currentRequestId (2)
,所以结果被丢弃。
- 第一个
-
2500ms后(自开始):
- 第二个
fetchData
的定时器触发。 - Promise 解析,结果添加到微任务队列。
- 第二个
-
微任务队列再次处理:
- 第二个
fetchData
的结果被处理。 - 这次
requestId (2) === currentRequestId (2)
,所以结果被接受。
- 第二个
这个过程展示了JavaScript的异步特性和事件循环机制。通过使用 currentRequestId
,我们能够跟踪最新的请求,并确保只处理最新ID对应的数据,即使在多个异步操作并发执行的情况下也能正确工作。