lisp语言Emacs LispEmacs

谈谈Emacs Lisp的异步

2016-07-09  本文已影响466人  齐格Insight

elisp的异步

elisp并没有提供好的异步操作,原生的语言级别只提供了一个创建异步子进程的函数。

异步子进程

elisp提供了一个创建异步子进程的函数start-process,这个函数返回一个process对象。该函数的语法如下:

start-process name buffer-or-name program &rest args

参数解释:

  1. name 表示子进程的名字,如果该名字对应的子进程已经存在。那么它将被修改为name<1>,这样使得它名字唯一。当然,如果name<1>也存在了,名字将被修改为name<2>,以此类推;
  2. buffer-or-name 与子进程相关联的buffer名字,并且,子进程的执行结果也会显示在这个buffer上;
  3. program 为要执行的程序,如果program为nil,那么不创建子进程,只创建一个buffer;同时,其他参数(args)也被无视;
  4. args 其他参数,将作为program的参数。

下面举一个例子:

(defun ab/test-start-process ()
  "test aysnc proecess"
  (interactive)
  (start-process "test-start-process" "*tsp*" "ls"
                 "-l" (file-truename "~/.spacemacs.d")))

然后,执行的结果将显示在*tsp*这个buffer里

异步子进程的结果*tsp*内容

process对象的操作

获得当前正在运行中的子进程列表,可采用list-processes这个函数,如下:

运行中的子进程列表
对start-process函数返回的process对象,elisp提供了以下操作函数:
get-process name
process-status process-name

它的参数可为子进程对象子进程名子进程对应的buffer。这个函数返回的结果可能为:run/stop/exit/singal/open/closed/connect/failed/listen/nil。

process-live-p process

如果子进程仍在alive,则返回non-nil。当一个子进程的状态为run, open, listen, connect 或 stop时,它被认为是alive的。

process-exit-status process

其他操作函数请参照手册。

async包

通过以上我们发现Emacs Lisp的异步操作真是很繁琐。要是我想异步地执行一个lambda函数?怎么办?
答:采用async这个异步包。该包可通过melpapopkit elpa软件源安装。
它显然是一个更好的选择。它的使用语法如下:

async-start START-FUNC FINISH-FUNC

其中第一个参数是子进程要执行的lambda函数,第二个参数为回调函数:
下面是其说明文档里给的一个例子:

(async-start
 ;; 在子进程中要执行的lambda函数
 (lambda ()
   (message "This is a test")
   (sleep-for 3)  ;; 休眠3s
   222)

 ;;当子进程执行完成后要执行的回调,子进程执行的结果将作为回调函数的参数
 (lambda (result)
   (message "Async process done, result should be 222: %s" result)))

如果去读async的源代码,你会发现它本质上调用的还是start-process这个函数。不过,它通过调用start-process启动了一个emacs子进程来执行lambda函数。所以,导致的一个问题是,通过async-start创建的emacs子进程并没有继承当前emacs运行环境的上下文(context)。

下面举一个我自己写的例子(用于异步更新本地repo):

(defun ab/git-code-update ()
  "update code async."
  (interactive)
  (async-start
   ;; 异步执行更新code操作
   (lambda ()
     (add-to-list 'load-path "~/.spacemacs.d/parts")
     (require 'aborn-log)
     (ab/log "exec-when-emacs-boot....")
     (let ((ab--git-project-list
            '("~/.emacs.d/" "popkit" "~/.spacemacs.d/" "piece-meal" "pelpa" "eden")))
       (dolist (elt ab--git-project-list)
         (let* ((working-directory
                 (if (or (string-prefix-p "/" elt) (string-prefix-p "~" elt))
                     elt
                   (concat "~/github/" elt "/")))
                (default-directory working-directory))
           (ab/log (shell-command-to-string "echo $PWD"))
           ;; 执行操作是异步的!
           (ab/log (shell-command-to-string "git pull"))))))
   (lambda (result)
     (message "finished ab/exec-when-emacs-boot,%s" result))))

在这个例子中aborn-log.el文件虽然已经在当前启动的emacs中load了,但在新创建的异步的emacs子进程中完全没有这个上下文(context)信息。因此,你不得不加入以下两行代码:

(add-to-list 'load-path "~/.spacemacs.d/parts")
(require 'aborn-log)

这样才能调用ab/log这个函数。

上一篇下一篇

猜你喜欢

热点阅读