概述
Vim從8.0版本開始支持異步IO,通過job來開始執行另一個進程,通過channel來進行進程通信。
只要Vim的版本高于8.0,并且在編譯時有+channel
和+job
的feature,就可以通過使用Vim的異步支持來讓插件或腳本擁有更好的體驗,比如異步編譯使Vim在編譯時不在阻塞、異步語法高亮來時Vim獲得更快的速度等。
使用
has('channel')
has('job')
來判斷Vim是否有相關支持。
我有一個在Vim中翻譯單詞的插件,但在網絡不好的情況下翻譯會阻塞正常的瀏覽,本文是我希望插件可以進行異步翻譯而進行的學習筆記,只簡單記錄了job的工作方式,更多詳細內容請在Vim中使用:help channel
查看。
job的工作方式
job可以理解為Linux中的進程,通過:
job_start(command, {options})
可以在這個一個進程(job)中執行command,job_start返回job對象,使用
let channel = job_getchannel(job)
可以獲得與job關聯的channel用于通信,本文不再討論。
command是要執行的外部命令,如果要獲取命令的執行結果或狀態,可以使用options設置回調函數來完成。
處理任務輸出
捕獲每次輸出
如果command產生輸出,可以使用out_cb
定義回調函數處理輸出:
func! Handler(channel, msg)
" deal with msg
endfunc
let job = job_start(command, {"out_cb", "Handler"})
cb表示callback
或者可以使用ch_read(job)
或者cd_readraw(job)
讀取job產生的輸出
捕獲結束輸出
如果不想處理任務的中間輸出,可以使用close_cb
定義回調函數獲取結束job的輸出:
func! CloseHandler(channel)
while ch_status(a:channel, {'part': 'out'}) == 'buffered'
echomsg ch_read(a:channel)
endwhile
endfunc
let job = job_start(command, {'close_cb': 'CloseHandler'})
處理任務的錯誤輸出
out_cb
指定的回調函數不會接收標準錯誤輸出,可以使用err_cb
指定的回調函處理錯誤輸出:
let job = job_start(command, {"out_cb": "Handler",
\ "err_cb": "ErrHandler"})
callback
指定的回調函數既可以接收錯誤輸出,也可以接收普通輸出
let job = job_start(command, {"callback": "MyHandler"})
控制job
得到job狀態:
job_status(job)
停止job:
job_stop(job)
示例
如果我們有一個耗時3s的外部程序要執行,程序執行之后輸出日期,如果使用過去的方法,將其映射到<F3>
鍵(這里使用sleep 3s模擬該耗時程序):
nnoremap <F3> :!/bin/bash -c 'sleep 3s; date'<CR>
點擊<F3>
后Vim將有3s處于阻塞狀態,無法進行任何操作。
接下來使用異步來處理這個程序:
" 回調函數
func! Handler(channel, msg)
echo a:msg
endfunc
" 執行job
func! GetDate()
call job_start(['/bin/bash', '-c', 'sleep 3s; date'], {'callback': 'Handler'})
endfunc
nnoremap <F3> :call GetDate()<cr>
此時再點擊<F3>
,Vim將不會阻塞,可以繼續進行操作,并在3s后輸出日期。