// ==================== 数据获取函数(带并发锁) ====================
let mexcFetching = false;
let binanceFetching = false;
async function fetchFromAPI(endpoint, source, cooldownSec, silentMode = false) {
if ((source === 'mexc' && mexcFetching) || (source === 'binance' && binanceFetching)) {
console.warn(`[自动获取] ${source.toUpperCase()} 上一次请求尚未完成,本次跳过`);
return;
}
if (source === 'mexc') mexcFetching = true;
else binanceFetching = true;
if (infoCard && !silentMode) infoCard.classList.add('hide');
const startTime = Date.now();
if (!silentMode) {
startCoolDown(source, cooldownSec);
}
if (!silentMode) {
resultDiv.innerHTML = '
⏳ 请求中,请稍候 (拉取K线数据)...
';
}
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 45000);
const response = await fetch(endpoint, {
method: 'GET',
headers: { 'Accept': 'application/json' },
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const json = await response.json();
if (!json || Object.keys(json).length === 0) throw new Error('后端返回空对象');
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
const elapsedText = `✅ 加载完毕 ${elapsed}s`;
if (source === 'mexc') {
mexcElapsed = elapsedText;
if (!silentMode) refreshCoolDownDisplay('mexc');
} else {
binanceElapsed = elapsedText;
if (!silentMode) refreshCoolDownDisplay('binance');
}
cachedData = json;
activeSource = source;
cachedDynamicMap = null; // 清除旧的动态指标缓存
// 静默预加载动态指标(不阻塞界面)
preloadDynamicIndicators();
// 如果是自动获取(silentMode = true),则静默保存到服务器
if (silentMode) {
saveAllDataToServer();
}
if (silentMode) {
console.log(`[自动获取] ${source.toUpperCase()} 数据已更新,共 ${Object.keys(json).length} 个币种,耗时 ${elapsed}s`);
if (currentMode !== null && cachedData) {
updateView();
}
} else {
resultDiv.innerHTML = `✅ ${source.toUpperCase()} 数据加载成功!共 ${Object.keys(json).length} 个币种
请点击上方标签查看数据
`;
if (currentMode !== null && cachedData) {
updateView();
}
}
} catch (err) {
console.error(err);
const errMsg = `❌ 错误: ${err.message}`;
if (!silentMode) {
if (source === 'mexc') {
if (timerMEXC) timerMEXC.textContent = errMsg;
setTimeout(() => {
if (mexcCountdown > 0 && timerMEXC && timerMEXC.textContent === errMsg) refreshCoolDownDisplay('mexc');
}, 3000);
} else {
if (timerBinance) timerBinance.textContent = errMsg;
setTimeout(() => {
if (binanceCountdown > 0 && timerBinance && timerBinance.textContent === errMsg) refreshCoolDownDisplay('binance');
}, 3000);
}
resultDiv.innerHTML = `❌ 请求失败: ${err.message}
请检查后端 ${endpoint} 是否正常
`;
} else {
console.warn(`[自动获取] ${source.toUpperCase()} 获取失败: ${err.message}`);
}
} finally {
if (source === 'mexc') mexcFetching = false;
else binanceFetching = false;
}
}