React useEffect中Promise.resolve未等待完成即执行后续代码的原因排查
为什么useEffect中Promise.then的逻辑晚于同步TVL计算执行?
这事儿其实是JavaScript异步执行机制的锅,我来给你拆解清楚:
问题根源:异步代码的执行顺序
JavaScript是单线程的,代码执行遵循同步优先,异步延后的规则。你代码里的Promise.resolve(prices).then(...)属于异步微任务,它的回调函数不会马上执行——哪怕prices已经是一个resolved的Promise,then里的逻辑也会被放到微任务队列里,等当前所有同步代码(也就是你的part2 TVL计算逻辑)全部执行完毕之后,才会被取出来执行。
所以在你的代码里,每个lpPool的遍历过程中:
- 先执行part1的同步部分(拆分token符号、调用fetchTokenPrice)
- 直接跳到part2执行TVL计算(这时候pool的
priceTokenZero和priceTokenOne还没被赋值,因为then里的逻辑还在队列里等着) - 等所有同步代码都跑完了,才会逐个执行then里的价格赋值逻辑
这就导致了你看到的“TVL计算先跑”的现象。
解决方案:用async/await确保顺序
要让价格获取完成后再计算TVL,你需要把整个逻辑改成异步等待的方式。不过注意:useEffect的回调函数不能直接标记为async,所以我们要在内部定义一个async函数然后调用它:
useEffect(() => { // 定义异步函数处理逻辑 const processPools = async () => { // 用for...of遍历,确保每个异步操作完成后再处理下一个 for (const lpPool of mainnet.Exchanges.Pancakeswap.LpTokens) { const [tokenZeroSymbol, tokenOneSymbol] = lpPool.name.replace(' LP', '').split('-'); // 等待价格获取完成,这里如果fetchTokenPrice本身返回Promise,就不用Promise.resolve包装了 const [priceTokenZero, priceTokenOne] = await fetchTokenPrice( tokenZeroSymbol.toLowerCase(), tokenOneSymbol.toLowerCase() ); // 找到对应的pool const targetPool = filteredFarmPools.find( pool => lpPool.name.replace(' LP', '') === pool.name ); if (targetPool) { // 先赋值价格 targetPool.priceTokenZero = priceTokenZero.usd; targetPool.priceTokenOne = priceTokenOne.usd; // 再计算TVL,这时候价格已经存在了 const tvl0 = (targetPool.reserveTokenZero / 10 ** 18) * targetPool.priceTokenZero; const tvl1 = (targetPool.reserveTokenOne / 10 ** 18) * targetPool.priceTokenOne; targetPool.tvl = tvl0 + tvl1; console.log('完成价格赋值和TVL计算'); } } }; // 调用异步函数 processPools(); }, [filteredFarmPools]); // 必须添加依赖项,避免无限循环和过期值
额外提醒:React状态的不可变性
如果filteredFarmPools是用useState声明的状态,那你不能直接修改对象属性(比如targetPool.priceTokenZero = ...),因为React依赖状态的引用来判断是否需要重新渲染。正确的做法是创建新的数组/对象来更新状态:
// 假设filteredFarmPools是state const [filteredFarmPools, setFilteredFarmPools] = useState([]); // 在processPools里改成这样更新 const updatedPools = [...filteredFarmPools]; const poolIndex = updatedPools.findIndex( pool => lpPool.name.replace(' LP', '') === pool.name ); if (poolIndex !== -1) { const tvl0 = (updatedPools[poolIndex].reserveTokenZero / 10 ** 18) * priceTokenZero.usd; const tvl1 = (updatedPools[poolIndex].reserveTokenOne / 10 ** 18) * priceTokenOne.usd; updatedPools[poolIndex] = { ...updatedPools[poolIndex], priceTokenZero: priceTokenZero.usd, priceTokenOne: priceTokenOne.usd, tvl: tvl0 + tvl1 }; setFilteredFarmPools(updatedPools); }
这样修改后,React才能检测到状态变化,重新渲染组件展示正确的TVL值。
内容的提问来源于stack exchange,提问作者Andrea D_
相关产品推荐
相关产品推荐

