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

HH : MM : SS
00 : 00 : 00


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

是否有任何本机软件包来实现这一目标?谢谢!

最佳答案

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


不应该,您谈论的是每秒更新一次,这还不算什么。

确保不信任该时间间隔,因为安排计时器与触发计时器之间的实际时间可能会因其他情况而异。因此,请务必始终计算从初始开始时间开始的剩余时间,而不是您为计时器指定的时间的累积。例如。:

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>

09-25 16:17