export class BenchmarkSuite { constructor() { this.state = 'initialized' this.cases = new CasesIterator([]) this.reset() this.onChange = () => {} } reset() { this.cases.reset() this.stats = this.cases.getInitialStats() } setBenchmarkType(benchmarktype) { this.benchmarktype = benchmarktype this.onChange() } setCases(cases) { this.cases = new CasesIterator(cases) this.stats = this.cases.getInitialStats() this.onChange() } addStat(name, val) { this.stats[name] = [...this.stats[name], val] } async measureNext() { let currentCase = this.cases.next() this.benchmarktype.setCase(currentCase) this.addStat(currentCase.name, await this.benchmarktype.run()) } async run() { while (this.state === 'running' && this.cases.hasNext()) { await this.measureNext() this.onChange() await freeEventLoop() } switch (this.state) { case 'running': this.state = 'finished' break case 'stop_requested': this.reset() this.state = 'initialized' break case 'pause_requested': this.state = this.cases.hasNext() ? 'paused' : 'finished' break } this.onChange() } start() { this.state = 'running' this.run() } pause() { this.state = 'pause_requested' } stop() { this.state = 'stop_requested' this.run() } } /** * Promise that uses setTimeout to resolve. * This is a hack to free the eventloop from microtasks. * Without this rendering blocks during benchmarks. */ async function freeEventLoop() { return new Promise(resolve => setTimeout(resolve, 0)) } class CasesIterator { constructor(cases) { this.cases = cases this.idx = -1 } current() { return this.cases[this.idx] } hasNext() { return this.idx + 1 < this.cases.length } next() { this.idx = this.idx + 1 return this.idx < this.cases.length ? this.current() : null } reset() { this.idx = -1 } getInitialStats() { let stats = {} for (let c of this.cases) { stats[c.name] = [] } return stats } }