setTimeout 与 setInterval 的最佳实践

#1

TL;DR(太长不看版)

请使用 https://github.com/littlee/eslint-plugin-clean-timer

背景

在 JavaScript 异步编程中,作者经常会写出各种各样的 bug,其中最常见的错误,都是由于忘记清除 setTimeout 或者 setInterval 创建的定时器引起的。

考虑下面这段代码,以一个简单的 React 组件为例

import { useEffect, useState } from 'react';
function App() {
  const [sec, setSec] = useState(0);
  useEffect(() => {
    setInterval(() => {
      setSec((s) => s + 1);
    }, 1000);
  }, []);
  return <p>{sec}</p>;
}

组件挂载后,每隔一秒会将 sec 的值加 1,看起来一切正常,但是,当组件被卸载的时候,setInterval 创建的定时器仍然在持续运行。

经验丰富的读者应该能马上想到,我们可以通过在 useEffect 返回的函数中清除这个定时器来修复这个问题。

改善后的代码如下:

import { useEffect, useState } from 'react';
function App() {
  const [sec, setSec] = useState(0);
  useEffect(() => {
    let timer = setInterval(() => {
      setSec((s) => s + 1);
    }, 1000);
    return () => {
      clearInterval(timer);
    };
  }, []);
  return <p>{sec}</p>;
}

问题到这里就完美解决了,但是,对于新手来说,常常需要在运行代码之后才会发现这个错误,而且,在一些没有开发者调试工具的环境(例如移动端 webview)下,类似的错误经常被遗漏。

读到这里,有兴趣的读者可以尝试在编辑器中对自己的某个项目源码进行搜索,如果 setIntervalclearInterval 的搜索结果数量不相等,那么,这个项目就很有可能隐藏了类似的错误,同理,也适用于 setTimeoutclearTimeout

因此,作者开发了一款 eslint 插件,让项目在编码期间,就可以消除类似的错误。

解决方法

eslint-plugin-clean-timer

github 包地址:https://github.com/littlee/eslint-plugin-clean-timer

使用

第一步,在项目目录中安装 eslint 并初始化(已存在 eslint 配置请跳过这一步)

npm i -D eslint
npx eslint --init

第二步,安装 eslint-plugin-clean-timer

npm i -D eslint-plugin-clean-timer

并添加一下配置到 eslint 配置中,关于配置文件位置请参考 https://eslint.org/docs/user-guide/configuring/

{
  "plugins": ["clean-timer"],
  "rules": {
    "clean-timer/assign-timer-id": 2
  }
}

此时,打开带有定时器代码的文件,我们可以看到编辑器会对相关代码进行提示,以 VSCode 为例,需要安装 ESLint 拓展

如果修改了配置文件之后没有生效,可以尝试按 Command(Ctrl)+Shift+P,在弹出面板中找到 ESLint: Restart ESLint Server 命令,并按回车键执行

除此之外,作者还提供了贴心的修复建议,点击 ESLint 提示的 Quick Fix…,执行第一个选项,可以自动插入一个赋值语句

Apr-01-2021 23-17-17.gif