javascript fetch api 实现请求数据预 cas(compare-and-swap)策略,核心在于利用 http 的 if-none-match 和 if-match 头配合服务端返回的 etag 标识。具体做法是:首次请求获取资源并缓存其 etag;后续更新前,先发送一个带有 if-none-match 头的条件 get 请求验证数据是否变更;若服务端返回 304 则直接使用本地缓存,若返回新数据则更新缓存并获取新的 etag;最后执行 put/patch 时携带 if-match 头确保原子性更新。

如何获取并缓存资源的 Etag 标识
在首次加载或需要刷新数据时,通过 Fetch 请求获取资源,并从响应头中提取 Etag 值进行存储。这一步是建立 CAS 基础的关键,因为 Etag 代表了数据在特定时间点的唯一快照。

// 模拟首次获取数据并缓存 Etag
async function fetchDataWithEtag(url) {
const response = await fetch(url);
const etag = response.headers.get('etag');
if (response.ok && etag) {
// 假设使用 localStorage 或内存对象缓存
cache[url] = { data: await response.json(), etag };
console.log(`Data cached with Etag: ${etag}`);
return cache[url].data;
}
throw new Error('Failed to fetch or no Etag provided');
}
上述代码展示了如何从响应头中读取 Etag 并存入缓存对象。实际应用中,Etag 通常由服务器根据内容生成,格式如 "abc123"。客户端需确保在每次成功获取新数据时更新对应的 Etag,以便后续请求使用。
如何利用 If-None-Match 验证数据变更
在执行写操作之前,必须先确认当前缓存的数据是否是最新的。通过发送带有 If-None-Match 头的 GET 请求,可以询问服务器缓存是否仍然有效。如果服务器认为数据未变,将返回 304 Not Modified,此时无需下载新数据,直接复用本地缓存即可。

// 验证数据是否变更
async function checkForUpdates(url, currentEtag) {
const response = await fetch(url, {
headers: {
'If-None-Match': currentEtag
}
});
if (response.status === 304) {
console.log('Data unchanged, using local cache.');
return null; // 表示无更新
} else if (response.ok) {
const newEtag = response.headers.get('etag');
const newData = await response.json();
// 更新缓存
cache[url] = { data: newData, etag: newEtag };
console.log(`Data updated. New Etag: ${newEtag}`);
return newData;
} else {
throw new Error('Verification failed');
}
}
这段代码演示了条件 GET 的逻辑。当 If-None-Match 头的值与服务端当前的 Etag 匹配时,服务端返回 304,表明客户端持有的副本是最新的。如果不匹配,服务端返回 200 及新数据,客户端需更新本地状态。这种机制避免了不必要的网络传输,同时保证了数据的最终一致性。
基于 If-Match 头执行原子性更新
当确定需要更新数据且验证通过后,执行实际的写入操作。此时必须使用 If-Match 头,将当前已知的 Etag 发送给服务器。服务器在接收 PUT 或 PATCH 请求时,会检查请求体对应的资源 Etag 是否与 If-Match 头中的值一致。如果一致,则执行更新并返回 200/204;如果不一致,说明在此期间有其他客户端修改了数据,服务器将拒绝更新并返回 412 Precondition Failed,从而实现了 CAS 语义。
// 执行原子性更新
async function updateDataAtomically(url, newData, currentEtag) {
try {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'If-Match': currentEtag
},
body: JSON.stringify(newData)
});
if (response.status === 200 || response.status === 204) {
const newEtag = response.headers.get('etag');
if (newEtag) {
cache[url] = { data: newData, etag: newEtag };
}
console.log('Update successful.');
return true;
} else if (response.status === 412) {
console.warn('Conflict detected. Data was modified by another client.');
// 处理冲突:重新获取最新数据并重试
await handleConflict(url);
return false;
} else {
throw new Error('Update failed');
}
} catch (error) {
console.error('Network error:', error);
return false;
}
}
async function handleConflict(url) {
// 重新获取最新数据
const freshData = await fetchDataWithEtag(url);
// 这里可以提示用户或自动重试逻辑
console.log('Fresh data fetched after conflict:', freshData);
}
此部分代码完整展示了 CAS 的最后一步。通过 If-Match 头,客户端显式声明“我只在我提供的版本基础上修改”。服务器端的校验确保了并发修改的安全性。如果发生冲突(412 错误),应用层需要决定如何处理,例如回滚、合并或提示用户。这种模式广泛应用于分布式系统和高并发 Web 应用中,以防止数据覆盖丢失。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











