谈谈Emacs Lisp的异步

作者: 齐格Insight | 来源:发表于2016-07-09 15:42 被阅读466次

    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提供了以下操作函数:
    • 通过子进程名获得process对象,如果进程不存在,则返回nil
    get-process name
    
    • 获得子进程的状态信息
    process-status process-name
    

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

    • 子进程是否alive
    process-live-p process
    

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

    • 子进程退出时的状态信息,如果子进程仍在alive,刚返回0
    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这个函数。

    相关文章

      网友评论

        本文标题:谈谈Emacs Lisp的异步

        本文链接:https://www.haomeiwen.com/subject/mzbrjttx.html