You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在setInterval中保持类上下文并修复倒计时暂停功能?

点击进度圈无法暂停倒计时的问题解决

问题背景

我实现了一个可倒计时的加载进度圈组件,完成倒计时后会执行指定回调。点击圆圈本应暂停倒计时,但当前实现无效——断点显示suspendCounter方法能触发,但后续setInterval仍忽略pauseCounter的变更。推测是类上下文绑定问题导致修改无法同步,不想使用全局变量,求可行解决方案。

相关代码

/**
 * class for creating a progress circle that is counting down
 * some callbacks can be passed to the constructor
 */
export class IntervalProgressCircle {
    #intervalID;

    /**
     * Creates the progress circle and mounts it to the parent element
     * @param {HTMLDivElement} parentEl - the parent element to mount the progress circle to
     * @param {number} seconds - the number of seconds the progress circle should take to complete
     * @param {function[]} callbacks - an array of functions to call when the progress circle completes
     */
    constructor( parentEl, seconds, ...callbacks ) {

        this.seconds = seconds;
        this.secondsElapsed = 0;
        this.callbacks = callbacks;
        this.pauseCounter = false;
        this.parentEl = parentEl;

        this.svgWrap = document.createElementNS(
            "http://www.w3.org/2000/svg", 'svg' );
        this.circle = document.createElementNS(
            "http://www.w3.org/2000/svg", 'circle' );

        this.#setStyle();

        this.radius = 32;
        this.circumference = this.radius * 2 * Math.PI;

        this.circle.style.strokeDasharray = `${ this.circumference } ${ this.circumference }`;
        this.circle.style.strokeDashoffset = `${ this.circumference }`;

        this.svgWrap.appendChild( this.circle );
        this.parentEl.appendChild( this.svgWrap );
    }

    setProgress( percent ) {
        this.circle.style.strokeDashoffset = ( this.circumference - percent / 100 * this.circumference ).toString();
    }

    suspendCounter() {
        this.pauseCounter = !this.pauseCounter;
    }

    startInterval() {
        /**
         * interval for reloading table data
         */
        if ( this.#intervalID ) {
            clearInterval( this.#intervalID );
        }
        this.#intervalID = setInterval( async () => {
            if ( this.pauseCounter === true ) return;
            if ( this.secondsElapsed > this.seconds ) {
                this.secondsElapsed = 1;
                await this.callbacks.forEach( cb => cb() );
                return;
            }
            this.setProgress( ( this.secondsElapsed / this.seconds ) * 100 );
            this.secondsElapsed += 1;
        }, 1000 );
    }

    #setStyle() {

        this.svgWrap.setAttributeNS( null, 'id', 'progress-ring' );
        this.svgWrap.setAttributeNS( null, 'class', 'progress-ring' );
        this.svgWrap.setAttributeNS( null, 'width', '5em' );
        this.svgWrap.setAttributeNS( null, 'height', '5em' );

        this.circle.setAttributeNS( null, 'stroke', 'white' );
        this.circle.setAttributeNS( null, "stroke-width", "0.4em" )
        this.circle.setAttributeNS( null, "fill", "transparent" )
        this.circle.setAttributeNS( null, "cx", "2.5em" )
        this.circle.setAttributeNS( null, "cy", "2.5em" )
        this.circle.setAttributeNS( null, "r", "2em" )
        this.circle.setAttributeNS(
            null, "style",
            "stroke-dasharray: 201.062px, " +
            "201.062px; stroke-dashoffset: 174.254px;" +
            "transition: 1.45s stroke-dashoffset;" +
            "transform: rotate(-90deg);" +
            "transform-origin: 50% 50%;"
        )
        this.circle.onclick = this.suspendCounter;
    }
}

解决方案

修改#setStyle方法中的点击事件绑定代码:

// 原代码
this.circle.onclick = this.suspendCounter;

// 修改后
this.circle.onclick = () => this.suspendCounter();

原理说明

直接将类方法suspendCounter赋值给onclick时,事件触发时的this会指向触发事件的DOM元素(即circle元素),而非IntervalProgressCircle的实例。此时在suspendCounter中修改的this.pauseCounter是DOM元素的属性,完全不影响类实例的pauseCounter,所以setInterval回调里的判断逻辑自然不会生效。

使用箭头函数包裹后,箭头函数不会创建自己的this上下文,而是继承外部作用域的this(也就是当前类实例)。调用suspendCounter时,方法内部的this正确指向类实例,修改的是实例的pauseCounter属性,setInterval回调就能读到更新后的值,从而实现点击暂停的功能。

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:35:28