Next.js应用运行1天后价格百分比异常及本地运行影响问询
Next.js价格百分比计算异常问题求助
问题描述
- 预期逻辑:代码中
priceHistory的month、week、day分别通过setInterval按月、周、日重置;运行1天后,日百分比(pctChange.day)应更新,周(pctChange.week)和月(pctChange.month)百分比应保持一致。 - 实际异常:运行1天后,周与日百分比一致,月百分比却出现差异,不符合预期。
疑问
本地机器频繁开关机,是否会影响内存中变量的存储,导致此异常?
已尝试操作
添加调试语句多次运行,仍未定位原因。
相关代码
import { NextResponse } from 'next/server'; let holdersCount: number | null = null; let priceData: { priceUsd: number, marketCap: string ,liquidity: string} = { priceUsd: 0, marketCap: '' ,liquidity: ''}; let priceHistory = { month: 0, // Use numbers day: 0, week: 0, }; let pctChange = { month: '0.00', // Keep as strings for output day: '0.00', week: '0.00', }; let isInitialFetch = true; {/* deleted the fetch holder function for simplicity*/} const fetchPriceData = async () => { try { const response = await fetch('https://api.dexscreener.com/latest/dex/tokens/0x9a26F5433671751C3276a065f57e5a02D2817973'); const data = await response.json(); priceData = { priceUsd: parseFloat(data.pairs[0].priceUsd) || 0, // Ensure it's always a valid number marketCap: data.pairs[0].marketCap, liquidity: data.pairs[0].liquidity.usd }; if (isInitialFetch) { // Only set the initial prices after the first successful fetch priceHistory.month = priceData.priceUsd; priceHistory.day = priceData.priceUsd; priceHistory.week = priceData.priceUsd; isInitialFetch = false; // Disable the flag after setting the initial prices console.log('inital state done') } console.log("Old price:", priceHistory.month); console.log("Current price:", priceData.priceUsd); // Calculate the percentage change for each timeframe const calculatePercentChange = (oldPrice: number, newPrice: number): string => { if (oldPrice === 0 || isNaN(oldPrice) || isNaN(newPrice)) { return '0'; // Avoid invalid calculations or division by 0 } const percentChange = ((newPrice - oldPrice) / oldPrice) * 100; return percentChange > 0 ? `+${percentChange.toFixed(2)}` : percentChange.toFixed(2); }; // Only calculate percentage change if the old prices have been set and are valid if (priceHistory.month > 0) { pctChange.month = calculatePercentChange(priceHistory.month, priceData.priceUsd); } if (priceHistory.day > 0) { pctChange.day = calculatePercentChange(priceHistory.day, priceData.priceUsd); } if (priceHistory.week > 0) { pctChange.week = calculatePercentChange(priceHistory.week, priceData.priceUsd); } console.log("Price data updated:", priceData); console.log("Percentage change:", pctChange); } catch (error) { console.error('Error fetching price data:', error); } }; // Fetch holders data every 8 hrs setInterval(fetchHoldersData, 8 * 60 * 60 * 1000); // Fetch price data every 8 hrs setInterval(fetchPriceData, 8 * 60 * 60 * 1000); // Reset the price history every hour, day, and week setInterval(() => { priceHistory.month = priceData.priceUsd; },31 * 24 * 60 * 60 * 1000); // Every month setInterval(() => { priceHistory.day = priceData.priceUsd; }, 24 * 60 * 60 * 1000); // Every day setInterval(() => { priceHistory.week = priceData.priceUsd; }, 7 * 24 * 60 * 60 * 1000); // Every week // Initial fetch fetchHoldersData(); fetchPriceData(); export async function GET() { return NextResponse.json({ holders: holdersCount, price: priceData.priceUsd.toFixed(2), marketCap: priceData.marketCap, liquidity: priceData.liquidity, percentChanges: pctChange, }); }
运行截图

问题分析与解决方案
1. 核心问题:内存变量生命周期与定时任务不可靠性
你的代码依赖内存全局变量存储priceHistory和pctChange,但Next.js API路由并非长期运行进程:
- 本地频繁开关机直接导致进程重启,内存中所有变量会被重置为初始值,
isInitialFetch重新变为true,此时priceHistory会被设为重启时的当前价格,而非保留重启前的历史基准值。 - 即使不重启,
setInterval在Node.js/Next.js环境中无法保证精确执行,进程休眠、资源回收都可能导致定时任务延迟或丢失。
2. 异常原因拆解
运行1天后的差异本质是进程重启导致priceHistory.month被重新初始化,而week/day的重置逻辑未按预期触发:
- 假设重启应用后,
isInitialFetch重置为true,新的priceHistory.month会被设为重启时的当前价格,而非原始初始价格。 priceHistory.day会从重启时间开始计算24小时后重置,priceHistory.week则需要7天,这就导致month的基准价格与week/day不一致,最终百分比计算出现差异。
3. 解决方案:持久化存储替代内存变量
必须将priceHistory的基准值持久化到外部存储(如文件、数据库、Redis),而非依赖内存:
示例修改思路(文件存储)
import fs from 'fs/promises'; import path from 'path'; const HISTORY_FILE_PATH = path.join(process.cwd(), 'price-history.json'); // 读取历史基准价格 async function loadPriceHistory() { try { const data = await fs.readFile(HISTORY_FILE_PATH, 'utf8'); return JSON.parse(data); } catch (err) { return { month: 0, day: 0, week: 0, timestamps: { month: 0, day: 0, week: 0 } }; } } // 保存历史基准价格 async function savePriceHistory(history) { await fs.writeFile(HISTORY_FILE_PATH, JSON.stringify(history), 'utf8'); } const fetchPriceData = async () => { try { const response = await fetch('https://api.dexscreener.com/latest/dex/tokens/0x9a26F5433671751C3276a065f57e5a02D2817973'); const data = await response.json(); priceData = { priceUsd: parseFloat(data.pairs[0].priceUsd) || 0, marketCap: data.pairs[0].marketCap, liquidity: data.pairs[0].liquidity.usd }; let priceHistory = await loadPriceHistory(); const now = Date.now(); // 初始化逻辑:无基准值时设置初始价格和时间戳 if (priceHistory.month === 0) { priceHistory.month = priceData.priceUsd; priceHistory.day = priceData.priceUsd; priceHistory.week = priceData.priceUsd; priceHistory.timestamps = { month: now, day: now, week: now }; await savePriceHistory(priceHistory); console.log('Initial state saved'); } // 基于时间戳判断是否需要重置基准(替代setInterval) const MONTH_MS = 31 * 24 * 60 * 60 * 1000; const DAY_MS = 24 * 60 * 60 * 1000; const WEEK_MS = 7 * 24 * 60 * 60 * 1000; if (now - priceHistory.timestamps.month >= MONTH_MS) { priceHistory.month = priceData.priceUsd; priceHistory.timestamps.month = now; await savePriceHistory(priceHistory); } if (now - priceHistory.timestamps.day >= DAY_MS) { priceHistory.day = priceData.priceUsd; priceHistory.timestamps.day = now; await savePriceHistory(priceHistory); } if (now - priceHistory.timestamps.week >= WEEK_MS) { priceHistory.week = priceData.priceUsd; priceHistory.timestamps.week = now; await savePriceHistory(priceHistory); } // 百分比计算逻辑不变 const calculatePercentChange = (oldPrice: number, newPrice: number): string => { if (oldPrice === 0 || isNaN(oldPrice) || isNaN(newPrice)) { return '0'; } const percentChange = ((newPrice - oldPrice) / oldPrice) * 100; return percentChange > 0 ? `+${percentChange.toFixed(2)}` : percentChange.toFixed(2); }; pctChange.month = calculatePercentChange(priceHistory.month, priceData.priceUsd); pctChange.day = calculatePercentChange(priceHistory.day, priceData.priceUsd); pctChange.week = calculatePercentChange(priceHistory.week, priceData.priceUsd); console.log("Price data updated:", priceData); console.log("Percentage change:", pctChange); } catch (error) { console.error('Error fetching price data:', error); } };
4. 额外优化建议
- 用时间戳判断替代
setInterval:避免定时任务的不可靠性,每次获取价格时主动检查是否需要重置基准。 - 生产环境建议使用Redis或数据库存储,比文件存储更可靠且支持并发访问。
内容的提问来源于stack exchange,提问作者vectorizedaynon
相关产品推荐
相关产品推荐

