43.12 空闲计时器

以下是设置在 Emacs 空闲一定时长后运行的计时器的方法。除创建方式外,空闲计时器的工作方式与普通计时器完全一致。

Command: run-with-idle-timer secs repeat function &rest args

创建一个计时器,使其在 Emacs 下次连续空闲 secs 秒时运行。secs 可以是数值,也可以是 current-idle-time 返回的类型值。

repeatnil,该计时器仅在 Emacs 首次满足空闲时长时执行一次。更常见的情况是 repeat 为非 nil,表示 每次 Emacs 空闲达到 secs 秒时均触发该计时器。

函数 run-with-idle-timer 返回一个计时器对象,可用于调用 cancel-timer(see 用于延迟执行的定时器)。

Emacs 在开始等待用户输入时进入 空闲(idle) 状态(通过超时等待输入的情况除外,see 读取单个事件),并持续至用户输入新内容。若计时器设为 5 秒空闲触发,则会在 Emacs 首次进入空闲约 5 秒后执行。即使 repeat 为非 nil,只要 Emacs 持续空闲,该计时器也不会重复触发,因为空闲时长会持续增加,不会回落至 5 秒。

Emacs 在空闲时可执行多种操作:垃圾回收、自动保存或处理子进程数据。但这些空闲期间的操作不会干扰空闲计时器,因为它们不会将空闲计时重置为零。一个设为 600 秒的空闲计时器,会在用户最后一次命令结束 10 分钟后触发,即便这 10 分钟内处理了数千次子进程输出、执行过垃圾回收与自动保存。

用户输入内容时,Emacs 在处理输入期间退出空闲状态;处理完成后再次进入空闲,所有设置为重复触发的空闲计时器会依次再次执行。

请勿编写包含循环的空闲计时器函数:在循环中逐次执行处理,并在 (input-pending-p)nil 时退出。这种写法看似自然,却存在两个问题:

同样,请勿在空闲计时器函数中创建另一个空闲计时器(包括自身),且其 secs 参数小于或等于当前已空闲的时间。此类计时器会近乎立即触发并持续反复执行,而非等待 Emacs 下次进入空闲。正确做法是在当前空闲时长基础上增加适当间隔后重新调度,如下例所示。

Function: current-idle-time

若 Emacs 处于空闲状态,该函数返回已空闲的时长,格式与 current-time(see 时刻)相同。

若 Emacs 未空闲,current-idle-time 返回 nil。可借此便捷判断 Emacs 是否空闲。

current-idle-time 的主要用途是让空闲计时器函数“暂停”一段时间。它可以创建另一个空闲计时器,在额外空闲数秒后再次调用自身。示例如下:

(defvar my-resume-timer nil
  "`my-timer-function' 用于重新调度自身的计时器,或为 nil。")

(defun my-timer-function ()
  ;; 若用户在 my-resume-timer 激活时输入命令,
  ;; 下次主空闲计时器调用此函数时,取消 my-resume-timer
  (when my-resume-timer
    (cancel-timer my-resume-timer))
  ...do the work for a while...
  (when taking-a-break
    (setq my-resume-timer
          (run-with-idle-timer
            ;; 计算比当前空闲时长多出 break-length 的时间
            (time-add (current-idle-time) break-length)
            nil
            'my-timer-function))))