deno學習(2)---從Hello World開始

deno的執(zhí)行非常簡單,使用deno xx.ts即可。 從deno hello.ts出發(fā),探究一下deno的運行機制。

  • 學習目標
    1. deno代碼的模塊
    2. 各模塊之間的交互模式

回顧

  • 在上一節(jié)學習過程中,已經知道了執(zhí)行make指令之后,會將go代碼、ts代碼、js代碼已經node_module代碼打包??梢源笾虏聹y一下,在整個deno項目里面,go會占一部分,js也會占一部分。作為js和go的交互模塊,v8worker也是很重要的一部分。

v8worker

  • v8worker本身是ry大神基于V8做的一個go和js調用的中間層。主體只有兩個功能:
    1. 接收消息
    • golang接收來自js的消息
//worker.go
func New(cb ReceiveMessageCallback) *Worker {
    //...
}
  • js接收來自golang的消息
V8Worker2.recv((ab: ArrayBuffer) => {
//...
}
  1. 發(fā)送消息給js
  • golang分送消息到js
//worker.go
func (w *Worker) SendBytes(msg []byte) error {
  • js分送消息到golang
V8Worker2.send(msg)

在deno里面,作者對v8worker做了簡單的封裝,在go和js層面分別做了一個 分發(fā)器。dispatch.godispatch.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);
}

通過以上代碼,就可以實現一個訂閱者模式,從而實現數據交互。值得一提的是,數據交互格式為protobuf。這種方式相當安全、高效。

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配置,預加載js代碼。形成bridger
    deno.Init()

    //執(zhí)行denoMain()方法
    deno.Eval("deno_main.js", "denoMain()")
    deno.Loop()
}
  1. 初始化 2. 執(zhí)行js 里面的 denoMain方法 3.執(zhí)行一個輪詢
  • main.go 。 ./cmd/main.go引用了 deno包。Init、Eval、Loop都位于main.go

  • Init

    //創(chuàng)建所需文件夾 在 ${home}/.deno   創(chuàng)建 src cache 兩個文件夾
    createDirs()
    //訂閱 os
    InitOS()
    //訂閱echox
    InitEcho()
    //訂閱timers
    InitTimers()
    //訂閱fetch
    InitFetch()


    //一個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 理解為一個C/S的架構。
        Client發(fā)送消息,Server端回復消息。
    */
    PubMsg("start", &Msg{
        Command:        Msg_START,
        StartCwd:       cwd,
        StartArgv:      workerArgs,
        StartDebugFlag: *flagDebug,
        StartMainJs:    main_js,
        StartMainMap:   main_map,
    })
    DispatchLoop()
}

執(zhí)行流程

  1. 初始化。
    • 下面四個方法,通過V8注冊方法,實現對js調用的訂閱。
    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:   
        }
    })

剩下三個大同小異,不做具體說明。

  - 加載 js文件
//會加載 /dist/main.js 文件
main_js = stringAsset("main.js")
//加載main.js,編譯并執(zhí)行。
err := worker.Load("/main.js", main_js)
  1. 執(zhí)行denoMain方法。
  • denoMain方法是 位于main.js 里面。實際上在打包之前,是位于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í)行編譯和運行ts代碼
    mod.compileAndRun();
  });

這里訂閱了start事件,等待觸發(fā)。觸發(fā)的時候,就是對ts文件做編譯、執(zhí)行的時候。

  1. 開始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,
    })
        //對消息輪詢,實現數據交換。
    DispatchLoop()
}

dispatch.go

func DispatchLoop() {
      // 對協程做監(jiān)控。
    //實際上,Pub會新建一個協程消息,這里做監(jiān)聽。有新信息就通過worker發(fā)送給js層。
    for {
        select {
        case msg := <-resChan:
            out, err := proto.Marshal(msg)
            err = worker.SendBytes(out)
        case <-doneChan:
            return
        }
}
image.png

總結

  • 目前來看deno是一個很簡單的執(zhí)行ts的程序。效率也不是很高。實際上對ts的解析和執(zhí)行都是在runtime時期運行的,相對于以前對typescript先編譯成js再使用的方式,是一種全新的嘗試。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數據結構(3).初始化時...
    歐辰_OSR閱讀 30,242評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,658評論 1 32
  • 1. 分布式系統核心問題 參考書籍:《區(qū)塊鏈原理、設計與應用》 一致性問題例子:兩個不同的電影院買同一種電影票,如...
    molscar閱讀 1,003評論 0 0
  • 如果你要問妹迷們:為什么喜歡好妹妹?答案有很多,總結起來有幾個關鍵因素: 1.接地氣的小清新民謠配上“無節(jié)操”的逗...
    了了君閱讀 7,552評論 13 60
  • 你是風雨 我卻把你當彩虹 你是夜空 我卻把你當霓虹 數不清的星子 睡不醒的夢 你說我傻 把星子當珍寶 把夢境當人生
    一泡日照閱讀 218評論 4 3

友情鏈接更多精彩內容