我经常使用 try 这个package 来尝试新的package, 但是无论是安装还是尝试新的package , 都需要调用 package-refresh-contents
, 这个又是个耗时的同步函数,每次阻塞着都觉得很麻烦,所以我用 emacs-async 对package-refresh-contents
进行了魔改, 并且抽象了一个宏
;;; http://lists.gnu.org/archive/html/help-gnu-emacs/2008-06/msg00087.html
(defmacro samray/measure-time (&rest body)
"Measure the time it takes to evaluate BODY."
`(let ((time (current-time)))
,@body
(message "%.06f" (float-time (time-since time)))))
(defmacro samray/async-function (current-func)
"Wrap function CURRENT-FUNC with `async`."
(make-thread (lambda ()
`(let ((elapsed-time (samray/measure-time (,current-func))))
(message "Run `%s` with thread, elapsed time is %s" ',current-func ,elapsed-time))))
)
(samray/async-function package-refresh-contents)
只不过现在这个宏只适用 package-refresh-contents
这种无参数的函数, 我想让其通用一点, 无论有没有参数都适用, 大家有啥建议么
post部分,或者干脆看auto-install.el ,
我十几年前就用auto-install.el异步安装了
你误解我的意思了,我想要实现的不是异步安装,而是通过 macro/advice
将一个现有的函数魔改成异步函数, 只是异步安装是我的一个例子而已
把原来那个函数copy出来,然后加上异步回调就可以了
似乎你还是没有理解我的意思,我想做的是用async
对原来的函数进行包装,可以随便包装新的函数,而不需要copy, 每次都copy 实在是太可怕
我知道你的意思,但是有些函数因为设计时没有考虑异步,函数之间耦合很紧,你开始只想异步一个函数,但是最后需要wrap一堆函数。
还不如copy关键函数做异步变动
这也是当年我重写yaoddmuse.el 而不是魔改oddmuse.el 魔改还不如重写一个,魔改太脏了
有些函数设计的时候没有考虑异步,如果我用 Emac26的thread呢,
(defmacro samray/async-function (current-func)
"Wrap function CURRENT-FUNC with `async`."
(make-thread (lambda ()
`(let ((elapsed-time (samray/measure-time (,current-func))))
(message "Run `%s` with thread, elapsed time is %s" ',current-func ,elapsed-time))))
)
不过这个macro 似乎有问题, 我又不知道问题出在哪里, 宏虽然强大,但很容易跑偏.
听起来很怪,应该有不需要调用的使用方式,但你没有发现。
这基本等于
M-! emacs -Q -f package-refresh-contents &
大概率不是你想象的那样,它很有可能不会正常工作,考虑到:
- 它不会管你的 package.el 配置,比如你用了 Melpa 的话
- 它不会影响当前的 Emacs 的状态
async.el 没有魔法。
我本意是通过 async/thread
来避免那些耗时函数阻塞, 比如安装package 时, 或者是使用 tramp, 似乎如你所说,并没有魔法
async.el 适合用来做些没有副作用的事情。
但是偶尔如果需要副作用,也是可以的, 以 async-start
为例:
(async-start START-FUNC &optional FINISH-FUNC)
START-FUNC
就是那个异步过程,如果里边需要当前 Emacs 的变量/函数/包,需要想办法一一导入。FINISH-FUNC
是回调函数,在这里让副作用生效。有多少工作量,就看依赖是不是很复杂了。
用它来实现 package 异步安装,恐怕不能如愿。因为安装需要 package-refresh-contents
的副作用,即需要用到 refresh 返回的数据,所以要等异步调用完成才能够开始安装。而不是启个线程/进程更新,然后马上安装:
(async-start
;; START-FUNC
(lambda ()
(package-refresh-contents))
;; FINISH-FUNC
(lambda ()
(package-read-all-archive-contents)))
(package-install 'foo-package)
这里的 install 在异步操作完成之前就执行了,没有达到预想的效果,除非放在 FINISH-FUNC
里。所以当把一个函数变成异步之后,原先的流程就要变了,而且你这个 async-function
还不知道异步过程什么时候结束,实用性就更低了。
另外,其实 package-refresh-contents
本身是有个异步参数的
(package-refresh-contents &optional ASYNC)
是的,参加 package-alist
变量,直接手动安装就不用刷新了
(package-install (package-desc-create :name n :version v :dir 'builtin :kind k :archive "melpa"))
对于一些primary函数,比如dired-do-copy引用的,emacs 26引入的thread没法解决阻塞的问题;tramp有一个thread-safe branch,看样子不是简单魔改就能解决的