deno的執(zhí)行非常簡(jiǎn)單,使用
deno xx.ts
即可淤井。 從deno hello.ts出發(fā)哄酝,探究一下deno的運(yùn)行機(jī)制。
- 學(xué)習(xí)目標(biāo)
- deno代碼的模塊
- 各模塊之間的交互模式
回顧
- 在上一節(jié)學(xué)習(xí)過程中瘟裸,已經(jīng)知道了執(zhí)行
make
指令之后,會(huì)將go代碼诵竭、ts代碼话告、js代碼已經(jīng)node_module代碼打包÷盐浚可以大致猜測(cè)一下沙郭,在整個(gè)deno項(xiàng)目里面,go會(huì)占一部分裳朋,js也會(huì)占一部分病线。作為js和go的交互模塊,v8worker也是很重要的一部分鲤嫡。
v8worker
- v8worker本身是ry大神基于V8做的一個(gè)go和js調(diào)用的中間層送挑。主體只有兩個(gè)功能:
- 接收消息
- golang接收來自js的消息
//worker.go
func New(cb ReceiveMessageCallback) *Worker {
//...
}
- js接收來自golang的消息
V8Worker2.recv((ab: ArrayBuffer) => {
//...
}
- 發(fā)送消息給js
- golang分送消息到j(luò)s
//worker.go
func (w *Worker) SendBytes(msg []byte) error {
- js分送消息到golang
V8Worker2.send(msg)
在deno里面,作者對(duì)v8worker做了簡(jiǎn)單的封裝暖眼,在go和js層面分別做了一個(gè) 分發(fā)器惕耕。
dispatch.go
和dispatch.ts
,分別提供了訂閱诫肠、發(fā)布功能司澎。
dispatch.go
func Sub(channel string, cb Subscriber) {
subscribers, ok := channels[channel]
subscribers = append(subscribers, cb)
channels[channel] = subscribers
}
func Pub(channel string, payload []byte) {
wg.Add(1)
resChan <- &BaseMsg{
Channel: channel,
Payload: payload,
}
}
dispatch.js
export function sub(channel: string, cb: MessageCallback): void {
let subscribers = channels.get(channel);
subscribers.push(cb);
}
export function pub(channel: string, payload: Uint8Array): null | ArrayBuffer {
const msg = pb.BaseMsg.fromObject({ channel, payload });
// ...
return send(ab);
}
通過以上代碼欺缘,就可以實(shí)現(xiàn)一個(gè)訂閱者模式,從而實(shí)現(xiàn)數(shù)據(jù)交互挤安。值得一提的是谚殊,數(shù)據(jù)交互格式為
protobuf
。這種方式相當(dāng)安全蛤铜、高效嫩絮。
main.go
- 通過下面代碼可以知道,
deno
的主入口就是 ./cmd/main.go文件
deno: msg.pb.go $(GO_FILES)
go build -o deno ./cmd
- ./cmd/main.go
package main
import (
"github.com/ry/deno"
)
func main() {
//初始化deno配置昂羡,預(yù)加載js代碼絮记。形成bridger
deno.Init()
//執(zhí)行denoMain()方法
deno.Eval("deno_main.js", "denoMain()")
deno.Loop()
}
- 初始化 2. 執(zhí)行js 里面的 denoMain方法 3.執(zhí)行一個(gè)輪詢
main.go 。 ./cmd/main.go引用了 deno包虐先。Init怨愤、Eval、Loop都位于main.go下
Init
//創(chuàng)建所需文件夾 在 ${home}/.deno 創(chuàng)建 src cache 兩個(gè)文件夾
createDirs()
//訂閱 os
InitOS()
//訂閱echox
InitEcho()
//訂閱timers
InitTimers()
//訂閱fetch
InitFetch()
//一個(gè)v8工作線程
worker = v8worker2.New(recv)
//加載main.js代碼.
//main.js就是Makefile里面通過
/**
#生成 dist/main.js 通過 ts文件 和 node_moduels
dist/main.js: $(TS_FILES) node_modules
./node_modules/.bin/tsc --noEmit # Only for type checking.
./node_modules/.bin/parcel build --out-dir=dist/ --log-level=1 --no-minify main.ts
打包生成的代碼蛹批。
*/
main_js = stringAsset("main.js")
//加載main.js
err := worker.Load("/main.js", main_js)
exitOnError(err)
//代碼的map撰洗,用于定位代碼.
main_map = stringAsset("main.map")
- Eval
// It's up to library users to call
// deno.Eval("deno_main.js", "denoMain()")
func Eval(filename string, code string) {
//執(zhí)行代碼
err := worker.Load(filename, code)
exitOnError(err)
}
- Loop
func Loop() {
cwd, err := os.Getwd()
check(err)
/**
可以將v8worker2 理解為一個(gè)C/S的架構(gòu)。
Client發(fā)送消息腐芍,Server端回復(fù)消息差导。
*/
PubMsg("start", &Msg{
Command: Msg_START,
StartCwd: cwd,
StartArgv: workerArgs,
StartDebugFlag: *flagDebug,
StartMainJs: main_js,
StartMainMap: main_map,
})
DispatchLoop()
}
執(zhí)行流程
- 初始化。
- 下面四個(gè)方法猪勇,通過V8注冊(cè)方法设褐,實(shí)現(xiàn)對(duì)js調(diào)用的訂閱。
InitOS()
//訂閱echox
InitEcho()
//訂閱timers
InitTimers()
//訂閱fetch
InitFetch()
os.go
//訂閱了os泣刹。提供了5種模式助析。
Sub("os", func(buf []byte) []byte {
switch msg.Command {
case Msg_CODE_FETCH:
case Msg_CODE_CACHE:
case Msg_EXIT:
case Msg_READ_FILE_SYNC:
//讀取文件
case Msg_WRITE_FILE_SYNC:
}
})
剩下三個(gè)大同小異,不做具體說明椅您。
- 加載 js文件
//會(huì)加載 /dist/main.js 文件
main_js = stringAsset("main.js")
//加載main.js外冀,編譯并執(zhí)行。
err := worker.Load("/main.js", main_js)
- 執(zhí)行denoMain方法掀泳。
- denoMain方法是 位于main.js 里面雪隧。實(shí)際上在打包之前,是位于main.ts下员舵。
(window as any)["denoMain"] = () => {
initTimers(); //初始化timer
initFetch(); //初始化fetch
//訂閱start事件脑沿。
dispatch.sub("start", (payload: Uint8Array) => {
runtime.setup(mainJs, mainMap);
//就是文件路徑
const inputFn = argv[0];
//模塊加載
const mod = runtime.resolveModule(inputFn, `${cwd}/`);
//執(zhí)行編譯和運(yùn)行ts代碼
mod.compileAndRun();
});
這里訂閱了start事件,等待觸發(fā)马僻。觸發(fā)的時(shí)候捅伤,就是對(duì)ts文件做編譯、執(zhí)行的時(shí)候巫玻。
- 開始Loop
main.go
func Loop() {
cwd, err := os.Getwd()
/**
觸發(fā)start事件丛忆。
*/
PubMsg("start", &Msg{
Command: Msg_START,
StartCwd: cwd,
StartArgv: workerArgs,
StartDebugFlag: *flagDebug,
StartMainJs: main_js,
StartMainMap: main_map,
})
//對(duì)消息輪詢,實(shí)現(xiàn)數(shù)據(jù)交換仍秤。
DispatchLoop()
}
dispatch.go
func DispatchLoop() {
// 對(duì)協(xié)程做監(jiān)控熄诡。
//實(shí)際上,Pub會(huì)新建一個(gè)協(xié)程消息诗力,這里做監(jiān)聽凰浮。有新信息就通過worker發(fā)送給js層。
for {
select {
case msg := <-resChan:
out, err := proto.Marshal(msg)
err = worker.SendBytes(out)
case <-doneChan:
return
}
}
總結(jié)
- 目前來看deno是一個(gè)很簡(jiǎn)單的執(zhí)行ts的程序苇本。效率也不是很高袜茧。實(shí)際上對(duì)ts的解析和執(zhí)行都是在runtime時(shí)期運(yùn)行的,相對(duì)于以前對(duì)typescript先編譯成js再使用的方式瓣窄,是一種全新的嘗試笛厦。