《elisp 每日一练》——定时器机制

在前面的一系列文章,我们介绍了hook和advice。现在我们开始进入下一个专题,关于定时器、外部进程调用的部分。Emacs本质上仍然是一个单线程同步的程序,一旦遇到比较耗时的操作就会卡死主界面。通过这一专题的相关技术,可以有效的缓解卡死的现象。

背景知识

我们首先要介绍的是里面的定时器机制。在常用的编程语言中我们都接触过定时器。它的使用非常简单,通过函数设置一个定时器,函数通常包括时间和回调。然后通过另一个函数启动定时器,在时间到达之后调用回调函数。

Emacs也是类似的。启动定时器的函数是 run-at-time。它的签名如下:

emacs-lisp 复制代码
(run-at-time TIME REPEAT FUNCTION &rest ARGS)
  • TIME 是相对时间,单位是秒;
  • REPEAT 表示是否重复触发函数,如果为nil 触发一次并销毁,如果是秒数,则表示每间隔这段时间重复触发
  • FUNCTION 表示触发的函数,后面的 ARGS 是传递给函数的参数

函数会返回一个定时器对象,如果 REPEAT 参数是nil,在触发一次之后会自动销毁。或者我们可以随时手动调用 cancel-timer。销毁定时器

使用 run-at-time 定义的定时器是在调用开始之后计时。Emacs还有一种空闲计时器,它是在用户停止操作一段时间之后执行操作。它用来执行一些后台的耗时操作,在用户不频繁操作之后即使操作耗时导致界面卡住,它的用户体验也会稍微好一点。

这种定时器使用函数 run-with-idle-timer。函数签名如下:

emacs-lisp 复制代码
(run-with-idle-timer SECS REPEAT FUNCTION &rest ARGS)

它的签名与 run-at-time 相同,但是要注意其中的 repeat 参数。run-at-time 中的 repeat 参数表示下一次经过多长时间后再触发,也是就说第一次触发后面触发的时间间隔可以不一样, repeat 参数实际上传入一个数值。但是 run-with-idle-timer 中没有这种机制, repeat 仅仅是一个bool 开关,用来表示下次是否重复。它不能用来控制下一次触发的时间间隔。

练习

我们来实现一个定时提醒喝水的功能,使用定时器定义一个时间,当时间到达之后提示用户该喝水休息了。当用户休息时,我们使用空闲定时器触发一个任务,统计当前编辑的buffer中的字数

emacs-lisp 复制代码
(defvar my-hydration-timer nil
  "定时喝水定时器对象")

(defvar my-idle-audit-timer nil
  "用户空闲时,静默统计字数的定时器")

(defun my-hydration-alert-callback()
  "定时触发提示,提醒用户该喝水了"
  (message "已经连续工作一段时间了,喝口水休息一下把"))

(defun my-start-hydration-timer()
  "开启一个每隔30秒触发一次的循环提醒定时喝水"
  (interactive)
  (when my-hydration-timer
	(cancel-timer my-hydration-timer))
  (setq my-hydration-timer (run-at-time 30 30 #'my-hydration-alert-callback)))

(defun my-silent-heavy-audit()
  "定时器回调函数,会统计当前字数并输出"
  (when (derived-mode-p 'text-mode 'prog-mode)
	(let ((word-count (count-words-region (point-min) (point-max))))
	  (message "当前buffer 中共 %d 个字" word-count))))

(defun my-start-idle-audit-timer()
  "开启空闲定时器, 用户停止输入2s后会触发"
  (interactive)
  (when my-idle-audit-timer
	(cancel-timer my-idle-audit-timer))
	(setq my-idle-audit-timer (run-with-idle-timer 2.0 t #'my-silent-heavy-audit)))

总结

我们今天主要介绍了两种定时器,一种常规的定时器用 run-at-time 启动。另一种是空闲定时器,用户空闲达到一定时间时触发,它使用 run-with-idle-timer 触发。

相关推荐
aluluka1 天前
《elisp 每日一练》——hook 与 advice 综合练习
emacs·lisp·elisp
Masimaro4 天前
《elisp 每日一练》——mode hook
emacs
智购科技自动售货机工厂4 天前
省电40%的底层逻辑:冷热双温售货机基于SaaS的智能温控算法~YH
单片机·嵌入式硬件·百度·架构·emacs
Masimaro7 天前
《elisp》 每日一练——hook 机制详解
emacs
Masimaro7 天前
《elisp 每日一练》——major mode 综合练习
emacs
aluluka10 天前
《elisp 每日一练》—— syntax table 与 font lock
emacs·lisp·elisp
Masimaro11 天前
《elisp 每日一练》—— 基于treesitter 的语法高亮
emacs
智购科技自动售货机工厂23 天前
数字人民币硬钱包支付失败,排查发现是NFC读卡器功率不足~YH
python·面试·架构·eclipse·emacs
Masimaro24 天前
《elisp 每日一练》—— define-derived-mode 管理 syntax-table
emacs