javascript - 如何在没有任何性能问题的情况下渲染计时器

标签 javascript react-native timer duration

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

HH : MM : SS
00 : 00 : 00

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

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

最佳答案

But my concern is will there be any performance issue if we run multiple state updates on continuous basis?

不应该有,你说的是每秒左右更新一次,这并不算多。

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

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>

关于javascript - 如何在没有任何性能问题的情况下渲染计时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60465294/

相关文章:

javascript - Tippy.js 在 IE 11 中产生不良影响

javascript - 在 React Native 移动应用程序中存储用户凭据的位置

javascript - API 调用不会返回数组中的所有对象

css - React Native 中行高的样式非常不寻常

javascript - RSQM - 手动轮询?

javascript - 使用 Lodash 根据另一个对象的真实状态获取对象的值

windows - Windows 10 下多核处理器上的 QueryPerformanceCounter 行为不稳定

c - 了解定时器和中断周期

javascript - AngularJS 中的计时器

javascript - 如何使用 Promise 来解决这个问题?