IOS Safari 时序问题导致刚进入的时候发出了空请求
问题现象
在 iOS Safari 浏览器中,页面首次加载时会发出一个异常的空请求:
text
URL: https://hyperhuman.deemos.com/api/task/cards
状态: —
来源: —
初始化程序: 557.4e48380e.js:2:1114582
请求头:
Accept: application/json, text/plain, /
Content-Type: application/json
Origin: https://hyper3d.ai
Referer: https://hyper3d.ai/该请求没有正确的 method 和 payload,导致请求失败。但奇怪的是:
- 代码中的错误处理没有捕获到这个异常
- 页面一直保持 loading 状态
- 切换 tab 重新发起请求时一切正常
- 只有这一个 API 有这个问题
问题分析
通过代码分析,发现这是一个典型的 React 组件初始化时序问题:
- 组件初始化时序
js
useDebounceEffect(() => {
if (cancelRequest) {
cancelRequest.cancel('CardType changed');
}
setAllLoaded(false)
setIsLoading(true)
pageRef.current = 0
if (cardsType === 'Story') {
// ...
} else {
requestAnimationFrame(() => {
setCards([])
})
setGalleryStatus('DEFAULT')
if (haveSearchTab) {
throttleFetchSearchCard()
} else {
throttleFetch()
}
setShowStorySidebar(true)
}
}, [cardsType, selectedTagKeys, haveSearchTab], 100)- 错误处理不完整
在 iOS Safari 中,组件的初始化时序与其他浏览器有细微差异,导致请求可能在必要的状态初始化之前就发出。
解决方案
- 添加初始化状态检查
javascript
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
const initialize = async () => {
await new Promise(resolve => setTimeout(resolve, 100));
setIsInitialized(true);
};
initialize();
}, []);
useDebounceEffect(() => {
if (!isInitialized) return;
// ... 原有逻辑
}, [cardsType, selectedTagKeys, haveSearchTab, isInitialized], 100);- 完善错误处理
javascript
const fetchCard = async (retryCount = 3) => {
try {
if (!cardsType || !pageRef.current) {
await new Promise(resolve => setTimeout(resolve, 100));
return fetchCard(retryCount);
}
const fetchRes = await getCards({ /*...*/ });
if (!fetchRes || !fetchRes.data) {
if (retryCount > 0) {
await new Promise(resolve => setTimeout(resolve, 1000));
return fetchCard(retryCount - 1);
}
throw new Error('Empty response received');
}
// 处理响应...
} catch (error) {
// 错误处理...
} finally {
setIsLoading(false);
}
};关键点总结
- iOS Safari 的组件初始化时序可能与其他浏览器不同
- 需要确保所有必要的状态都已初始化后再发起请求
- 添加适当的重试机制
- 完善错误处理,覆盖所有异常情况
- 确保 loading 状态能正确重置
经验教训
- 在处理跨浏览器兼容性时,需要特别注意初始化时序问题
- 请求处理要遵循"防御性编程"原则,处理所有可能的异常情况
- 添加合适的重试机制和状态重置逻辑
- 在开发时多关注不同浏览器的行为差异
这个问题也提醒我们,在前端开发中要特别注意浏览器兼容性和异步操作的时序问题,同时保持良好的错误处理实践。