如何在React中为轮盘赌模拟函数添加可实时更新的进度条
核心问题说明
你当前的模拟函数为同步执行,JavaScript单线程运行特性会导致大计算量执行期间,浏览器主线程被完全占用,无法响应任何UI渲染操作,因此进度条无法实时更新。
实现方案
我们将同步模拟逻辑改造为异步分批次执行,每完成1%的计算量就更新一次进度,同时让出主线程给浏览器渲染UI,既保证进度实时同步,也不会阻塞用户交互。
第一步:改造模拟函数
改造原同步函数为异步版本,支持传入进度回调,同时优化内存占用(无需存储全量模拟结果):
// 改造后的异步轮盘赌模拟函数 const SimulateEuroRouletteAsync = async ( starting_balance, wagering, bet_size, simulations, onProgress // 进度回调,入参为0-100的整数进度值 ) => { let bust_out_count = 0 let balance_total = 0 let wagering_total = 0 // 按1%比例拆分计算批次 const batchSize = Math.max(1, Math.floor(simulations / 100)) let processed = 0 for(let batch = 0; batch < 100; batch++){ // 执行当前批次模拟 const batchEnd = Math.min(processed + batchSize, simulations) for(let i = processed; i < batchEnd; i++){ let balance = starting_balance let wagering_complete = 0 let bust_out = false while(bet_size <= balance && wagering_complete < wagering){ // 保留原有随机数生成逻辑,注意random.int需要为你项目中已实现的方法 const random_int = random.int(0, 36) balance -= bet_size if(random_int > 18){ balance += bet_size * 2 } wagering_complete += bet_size } bust_out = balance < bet_size && wagering > wagering_complete if(bust_out) bust_out_count++ else balance_total += balance wagering_total += wagering_complete processed++ } // 更新进度 onProgress?.(Math.min(Math.round((processed / simulations) * 100), 100)) // 让出主线程给浏览器渲染UI await new Promise(resolve => setTimeout(resolve, 0)) if(processed >= simulations) break } return { ev: balance_total / simulations, average_wagered: wagering_total / simulations, bust_out_rate: ((bust_out_count / simulations) * 100), simulated: processed } }
第二步:React组件接入进度条
在组件中维护进度状态,调用异步模拟函数实现进度同步更新:
import { useState } from 'react' const RouletteSimulator = () => { // 模拟参数可替换为你的表单输入值 const [params] = useState({ starting_balance: 1000, wagering: 5000, bet_size: 10, simulations: 1000000 }) const [progress, setProgress] = useState(0) const [result, setResult] = useState(null) const [isRunning, setIsRunning] = useState(false) const startSimulate = async () => { setIsRunning(true) setProgress(0) setResult(null) const simResult = await SimulateEuroRouletteAsync( params.starting_balance, params.wagering, params.bet_size, params.simulations, setProgress ) setResult(simResult) setIsRunning(false) } return ( <div style={{padding: '20px'}}> <button onClick={startSimulate} disabled={isRunning}> {isRunning ? '模拟运行中' : '开始模拟'} </button> {/* 进度条UI */} {isRunning && ( <div style={{margin: '16px 0', height: '24px', background: '#eee', borderRadius: '12px', overflow: 'hidden'}}> <div style={{height: '100%', width: `${progress}%`, background: '#1677ff', transition: 'width 0.1s linear'}} /> <span style={{position: 'relative', top: '-22px', left: '50%', transform: 'translateX(-50%)'}}> {progress}% </span> </div> )} {/* 结果展示 */} {result && ( <div style={{marginTop: '20px'}}> <p>预期回报:{result.ev.toFixed(2)}</p> <p>平均投注额:{result.average_wagered.toFixed(2)}</p> <p>破产率:{result.bust_out_rate.toFixed(2)}%</p> </div> )} </div> ) } export default RouletteSimulator
可选优化
- 若觉得100次进度更新太频繁,可调整批次拆分比例,比如每5%更新一次,减少重渲染开销
- 模拟量超过1000万次时,可使用Web Worker在后台线程执行计算,完全不占用主线程资源
- 可将
setTimeout替换为requestIdleCallback,利用浏览器空闲时间执行计算,进一步降低对交互的影响
内容的提问来源于stack exchange,提问作者willmahoney
相关产品推荐
相关产品推荐

