【问题标题】:How to render Timer without any performance issue如何在没有任何性能问题的情况下渲染 Timer
【发布时间】:2020-02-29 12:47:33
【问题描述】:

当用户点击特定按钮时,计时器应运行以计算用户执行任务的时间。计时器应采用以下格式

HH : MM : SS
00 : 00 : 00

计时器应从 0 开始,1 小时后结束。据我所知,这可以通过使用多个状态更新和setIntervalsetTimeoutclearInterval 来实现。但我担心的是,如果我们连续运行多个状态更新,会不会出现任何性能问题?

是否有任何 react-native 包来实现这一点?谢谢!

【问题讨论】:

    标签: javascript react-native timer duration


    【解决方案1】:

    但我担心的是,如果我们连续运行多个状态更新,会不会出现任何性能问题?

    不应该,你说的是每秒钟左右更新一次,这不是那么多。

    请务必不要相信时间间隔,因为您安排计时器和触发计时器之间的实际时间可能会因其他情况而有很大差异。因此,请务必始终计算从初始开始时间开始的剩余时间,而不是您为计时器提供的时间的累积。例如:

    timerCallback() {
        this.setState({remaining: Date.now() - this.timerStarted});
    }
    

    或类似的。

    这是一个非常粗略的示例,说明为什么您总是重新计算而不是从剩余时间中减去间隔:

    function zeroPad(x, width) {
        return String(x).padStart(width, "0");
    }
    
    class Time extends React.Component {
        render() {
            let time = Math.round(this.props.time / 1000);
            const seconds = time % 60;
            time -= seconds;
            const minutes = Math.floor(time / 60) % 60;
            time -= minutes * 60;
            const hours   = Math.floor(time / 3600);
            return [
                <span>{zeroPad(hours,   2)}</span>,
                ":",
                <span>{zeroPad(minutes, 2)}</span>,
                ":",
                <span>{zeroPad(seconds, 2)}</span>
            ];
        }
    }
    
    class BadTimer extends React.Component {
        constructor(props) {
            super(props);
            this.interval = 1000;
            this.state = {
                running: false,
                remaining: 0,
                actualDuration: 0
            };
            this.start = this.start.bind(this);
            this.timerCallback = this.timerCallback.bind(this);
        }
    
        componentDidMount() {
            if (this.props.autoStart) {
                this.start();
            }
        }
    
        start() {
            this.setState(({running}) => {
                if (!running) {
                    this.started = Date.now(); // Just so we can show actual duration later
                    setTimeout(this.timerCallback, this.interval);
                    return {running: true, remaining: this.props.length};
                }
            });
        }
    
        timerCallback() {
            this.setState(({running, remaining}) => {
                if (running) {
                    // Don't do this
                    remaining = Math.max(remaining - this.interval, 0);
                    if (remaining <= 500) {
                        return {running: false, remaining, actualDuration: Date.now() - this.started};
                    }
                    setTimeout(this.timerCallback, this.interval);
                    return {remaining};
                }
            });
        }
    
        render() {
            const {running, remaining, actualDuration} = this.state;
            return (
                <span>
                    {running ? <Time time={remaining} /> : <input type="button" value="Start" onClick={this.start} />}
                    {actualDuration ? <span> Actual duration: <Time time={actualDuration} /></span> : false}
                </span>
            );
        }
    }
    
    class GoodTimer extends React.Component {
        constructor(props) {
            super(props);
            this.interval = 1000;
            this.state = {
                started: 0,
                update: 0,
                actualDuration: 0
            };
            this.start = this.start.bind(this);
            this.timerCallback = this.timerCallback.bind(this);
        }
    
        componentDidMount() {
            if (this.props.autoStart) {
                this.start();
            }
        }
    
        start() {
            this.setState(({started, update}) => {
                if (!started) {
                    setTimeout(this.timerCallback, this.interval);
                    started = Date.now();
                    return {started};
                }
            });
        }
    
        timerCallback() {
            // Do this instead
            this.setState(({started, update}) => {
                if (started) {
                    const remaining = this.getRemaining(started);
                    if (remaining <= 500) {
                        return {started: 0, actualDuration: Date.now() - started};
                    }
                    setTimeout(this.timerCallback, this.interval);
                    return {update: update + 1};
                }
            });
        }
    
        getRemaining(started) {
            return this.props.length - (Date.now() - started);
        }
    
        render() {
            const {started, actualDuration} = this.state;
            const remaining = this.getRemaining(started);
            return (
                <span>
                    {started ? <Time time={remaining} /> : <input type="button" value="Start" onClick={this.start} />}
                    {actualDuration ? <span> Actual duration: <Time time={actualDuration} /></span> : false}
                </span>
            );
        }
    }
    
    ReactDOM.render(
        <div>
            <div>
                <label>Bad:</label><BadTimer autoStart={true} length={15000} />
            </div>
            <div>
                <label>Good:</label><GoodTimer autoStart={true} length={15000} />
            </div>
        </div>,
        document.getElementById("root")
    );
    
    // Throw some delays in
    for (let n = 3000; n < 12000; n += 3000) {
        setTimeout(() => {
            console.log("Thread is busy...");
            const busy = Date.now();
            setTimeout(() => {
                // Do something that takes some time
                const stop = Date.now() + (Math.random() * 2000) + 700;
                while (Date.now() < stop) {
                    // Loop
                }
                console.log(`...done, was busy ${Date.now() - busy}ms`);
            }, 0);
        }, n);
    }
    body {
        font-size: 16px;
    }
    label {
        width: 48px;
        display: inline-block;
    }
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多