JavaScript有没有内置的方法来检测过时的异步结果?
我在处理异步JavaScript时,遇到了一个常见模式:多个异步调用可能会重叠并返回顺序错乱。例如
let currentQuery = "";
async function search(query) {
currentQuery = query;
const result = await fetch(`/api?q=${query}`);
const data = await result.json();
if (query !== currentQuery) {
// ignore stale result
return;
}
display(data);
}
上面的代码能工作,但它需要手动跟踪状态(currentQuery)以避免使用过时的结果。那么JavaScript是否提供内置机制来检测或取消过时的异步结果?除了像这样手动跟踪标识符之外,还有其他办法吗?
解决方案
我尝试过并查看了一些其他方法。正如一些评论者所说,JavaScript没有内置的方式来检测或自动忽略过时的异步结果。由于“过时”是应用层定义的,而不是语言概念,因此必须在代码中明确处理。想看看人们采用的不同实现方式,我在这里分享几种:
一种替代方法是使用 AbortController 来取消请求本身,但它并非在所有地方都有效,只对 fetch 有效:
let controller;
async function search(query) {
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api?q=${query}`, {
signal: controller.signal
});
const data = await res.json();
display(data);
} catch (e) {
if (e.name === 'AbortError') return;
throw e;
}
}
题中使用的方法是标准做法,但更干净的做法是使用一个请求标识符(request id):
let requestId = 0;
async function search(query) {
const id = ++requestId;
const res = await fetch(`/api?q=${query}`);
const data = await res.json();
if (id !== requestId) return;
display(data);
}
另一种方法是去重,尤其适用于缓存层和API。它并不能真正解决“过时”问题,但它可以防止重复请求,并且与上面的更清晰做法结合时,可以同时防止重复和过时的结果:
let version = 0;
const inFlight = new Map();
async function search(query) {
const v = ++version;
if (inFlight.has(query)) {
const data = await inFlight.get(query);
if (v !== version) return;
display(data);
return;
}
const promise = fetch(`/api?q=${query}`)
.then(res => res.json())
.finally(() => inFlight.delete(query));
inFlight.set(query, promise);
const data = await promise;
if (v !== version) return;
display(data);
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。