このチャプターの目次
起動時に必要なパッケージ以外を片っ端から autoload
してもまだ起動に時間がかかる (起動時に必要なパッケージだけでもそれなりにある) 場合は、それらを「少しずつ」セットアップしていくと快適です。
すなわち、全てのセットアップが完了してから初めてユーザーの入力を受け付けるのではなく、ユーザーの入力も受けつつ裏で並行してセットアップを進めることで、体感の起動速度=待たされ感が改善します。
タイマーを利用した擬似非同期
現在の Emacs で擬似非同期を行う手頃な方法は、タイマーです。以下のように after-init-hook
あたりでタイマーをスタートさせて、各設定項目を細切れに処理してゆくことで擬似的に非同期を実現します。
;; 非同期に行う設定のリスト
(defvar my-delayed-configurations nil)
;; 0.1 秒ずつ間隔を開けながら消化
(defvar my-delayed-configuration-timer nil)
(add-hook 'after-init-hook
(lambda ()
(setq my-delayed-configuration-timer
(run-with-timer
0.1 0.1 ; 0.1 秒ごとに
(lambda ()
(if my-delayed-configurations ; まだやることがあれば
(eval (pop my-delayed-configurations)) ; 一個やる
(cancel-timer my-delayed-configuration-timer)))))))
タイマーで少しずつ間隔を開けながら処理することで、ユーザーからの入力を (Emacs が) 処理する余地が生まれるので、体感の待たされ感が軽減します。
「非同期に実行する設定のリスト」に要素を追加するためのマクロを用意しておくと便利です。
(defmacro with-delayed-execution (&rest body)
(declare (indent 0))
`(push ',(cons 'progn body) my-delayed-configurations))
(with-delayed-execution
(require 'foo)
(foo-mode 1))
最近入ったスレッド機能を使うことも考えられますが、まだ不安定そうな雰囲気もあるので、私はタイマーを使っています。スレッドを使っても現状は結局ノンプリエンプティブなので、ユーザーの入力を少しずつブロックしながらやっていくことには変わりなくて、タイマーに対してさほど優位点がないように思います。