Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 当我不再需要时,如何清理这个“wait nextEvent(元素,';mousemove';)”模式?_Javascript_Reactjs_Typescript_Memory Leaks_Code Cleanup - Fatal编程技术网

Javascript 当我不再需要时,如何清理这个“wait nextEvent(元素,';mousemove';)”模式?

Javascript 当我不再需要时,如何清理这个“wait nextEvent(元素,';mousemove';)”模式?,javascript,reactjs,typescript,memory-leaks,code-cleanup,Javascript,Reactjs,Typescript,Memory Leaks,Code Cleanup,我有一个React组件,其中包含如下代码: class MyComponent extends React.Component { // ... trackStats = false componentDidMount() { this.monitorActivity() } componentWillUnmount() { this.trackStats = false } async monito

我有一个React组件,其中包含如下代码:

class MyComponent extends React.Component {
    // ...

    trackStats = false

    componentDidMount() {
        this.monitorActivity()
    }

    componentWillUnmount() {
        this.trackStats = false
    }

    async monitorActivity() {
        this.trackStats = true

        while (this.trackStats && this.elRef.current) {
            // elRef is a React ref to a DOM element rendered in render()
            await Promise.race([
                nextEvent(this.elRef.current, 'keydown'),
                nextEvent(this.elRef.current, 'click'),
                nextEvent(this.elRef.current, 'mousemove'),
                nextEvent(this.elRef.current, 'pointermove'),
            ])

            this.logUserActivity()
        }
    }

    logUserActivity() {
        // ...
    }

    render() { /* ... */ }
}

const nextEvent = (target, eventName) => new Promise(resolve => {
    target.addEventListener(eventName, resolve, { once: true })
})
问题是,如果卸载此组件,则添加到this.elRef.current所引用的DOM元素上的事件处理程序将保留在内存中,因为用户将不再与不再位于DOM中的元素交互

因此,while循环将被卡住,等待下一个事件,而下一个事件永远不会发生,因为while循环仍在等待最后一个事件,我相信这将导致
MyComponent
的实例在内存中泄漏

或者发动机是否足够聪明,可以将其清理干净?如果我对这些东西都没有可访问的引用,并且唯一链接的是while循环的范围,它正在等待一些承诺的实现,那么引擎会放弃它吗?或者它会让while循环作用域继续运行,等待承诺吗


如果while循环仍然存在(我猜是的),我应该如何清理它呢?

啊,有趣的用例!这似乎是一个很好的用例:


啊,有趣的用例!这似乎是一个很好的用例:


多亏了Surma的指导,我想出了一种在卸载组件时彻底清理的方法:

class MyComponent extends React.Component {
    // ...

    trackStats = false
    statsAbort = undefined

    componentDidMount() {
        this.monitorActivity()
    }

    componentWillUnmount() {
        this.trackStats = false
        this.statsAbort.abort()
    }

    async monitorActivity() {
        this.trackStats = true

        while (this.trackStats && this.elRef.current) {
            this.statsAbort = new AbortController

            try {
                // elRef is a React ref to a DOM element rendered in render()
                await Promise.race([
                    nextEvent(this.elRef.current, 'keydown'),
                    nextEvent(this.elRef.current, 'click'),
                    nextEvent(this.elRef.current, 'mousemove'),
                    nextEvent(this.elRef.current, 'pointermove'),
                ])
            } catch(e) {
                if (e.message !== 'abort_stats') throw e
            }

            this.statsAbort.abort()

            this.logUserActivity()
        }
    }

    logUserActivity() {
        // ...
    }

    render() { /* ... */ }
}

const nextEvent = (target, eventName, abortSignal) => new Promise((resolve, reject) => {
    target.addEventListener(eventName, resolve, { once: true })
    abortSignal.addEventListener("abort", () => {
      target.removeEventListener(eventName, resolve)
      reject(new Error('abort_stats'))
    });
})
但是直接使用addEventListener更简单,因此我决定使用以下内容,这对于本用例来说也更容易理解:

class MyComponent extends React.Component {
    // ...

    componentDidMount() {
        const el = this.elRef.current
        el.addEventListener('keydown', this.logUserActivity)
        el.addEventListener('click', this.logUserActivity)
        el.addEventListener('mousemove', this.logUserActivity)
        el.addEventListener('pointermove', this.logUserActivity)
    }

    componentWillUnmount() {
        const el = this.elRef.current
        el.removeEventListener('keydown', this.logUserActivity)
        el.removeEventListener('click', this.logUserActivity)
        el.removeEventListener('mousemove', this.logUserActivity)
        el.removeEventListener('pointermove', this.logUserActivity)
    }

    logUserActivity() {
        // ...
    }

    render() { /* ... */ }
}

多亏了Surma的指导,我想出了一种在卸载组件时彻底清理的方法:

class MyComponent extends React.Component {
    // ...

    trackStats = false
    statsAbort = undefined

    componentDidMount() {
        this.monitorActivity()
    }

    componentWillUnmount() {
        this.trackStats = false
        this.statsAbort.abort()
    }

    async monitorActivity() {
        this.trackStats = true

        while (this.trackStats && this.elRef.current) {
            this.statsAbort = new AbortController

            try {
                // elRef is a React ref to a DOM element rendered in render()
                await Promise.race([
                    nextEvent(this.elRef.current, 'keydown'),
                    nextEvent(this.elRef.current, 'click'),
                    nextEvent(this.elRef.current, 'mousemove'),
                    nextEvent(this.elRef.current, 'pointermove'),
                ])
            } catch(e) {
                if (e.message !== 'abort_stats') throw e
            }

            this.statsAbort.abort()

            this.logUserActivity()
        }
    }

    logUserActivity() {
        // ...
    }

    render() { /* ... */ }
}

const nextEvent = (target, eventName, abortSignal) => new Promise((resolve, reject) => {
    target.addEventListener(eventName, resolve, { once: true })
    abortSignal.addEventListener("abort", () => {
      target.removeEventListener(eventName, resolve)
      reject(new Error('abort_stats'))
    });
})
但是直接使用addEventListener更简单,因此我决定使用以下内容,这对于本用例来说也更容易理解:

class MyComponent extends React.Component {
    // ...

    componentDidMount() {
        const el = this.elRef.current
        el.addEventListener('keydown', this.logUserActivity)
        el.addEventListener('click', this.logUserActivity)
        el.addEventListener('mousemove', this.logUserActivity)
        el.addEventListener('pointermove', this.logUserActivity)
    }

    componentWillUnmount() {
        const el = this.elRef.current
        el.removeEventListener('keydown', this.logUserActivity)
        el.removeEventListener('click', this.logUserActivity)
        el.removeEventListener('mousemove', this.logUserActivity)
        el.removeEventListener('pointermove', this.logUserActivity)
    }

    logUserActivity() {
        // ...
    }

    render() { /* ... */ }
}

abort
在这些行中指的是什么:
nextEvent(someButton,“click”,abort.signal)
?@artem它应该是abortController,我做了编辑。
abort
在这些行中指的是什么:
nextEvent(someButton,“click”,abort.signal)
?@artem它应该是abortController,我做了编辑。