Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created April 22, 2024 22:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mizchi/e8e9946577bbb8e64405b738a2384bf8 to your computer and use it in GitHub Desktop.
Save mizchi/e8e9946577bbb8e64405b738a2384bf8 to your computer and use it in GitHub Desktop.

setTimeout binding

deno runner

import { flush, js_string, setMemory, spectest } from "../.mooncakes/mizchi/js_io/mod.ts";

type Instance = {
  fire: (fid: number) => void;
};

const initJs = () => {
  let instance: Instance;
  return {
    set<I extends Instance>(i: I) {
      instance = i;
    },
    setInterval: (fid: number, ms: number) => setInterval(() => instance.fire(fid), ms),
    clearInterval,
    setTimeout: (fid: number, ms: number) => setTimeout(() => instance.fire(fid), ms),
    clearTimeout,
  }
}

const js = initJs();

const { instance: { exports } } = await WebAssembly.instantiateStreaming(
  fetch(new URL("../target/wasm-gc/release/build/main/main.wasm", import.meta.url)),
  { js_string, spectest, js }
);

const {
  run,
  fire,
  _start,
  ["moonbit.memory"]: memory,
} = exports as any;

_start();

setMemory(memory);
js.set({ fire });

run();
eflush();

moonbit

fn js_set_timeout(fid : Int, timeout : Int) -> Int = "js" "setTimeout"

fn js_clear_timeout(tid : Int) -> Unit = "js" "clearTimeout"

fn js_set_interval(fid : Int, timeout : Int) -> Int = "js" "setInterval"

fn js_clear_interval(tid : Int) -> Unit = "js" "clearInterval"

type FnId Int derive(Eq)

pub fn FnId::hash(self : FnId) -> Int {
  self.0
}

type TimeoutId Int derive(Eq)

pub fn TimeoutId::new(i : Int) -> TimeoutId {
  TimeoutId(i)
}

pub fn TimeoutId::hash(self : TimeoutId) -> Int {
  self.0
}

type IntervalId Int derive(Eq)

pub fn IntervalId::new(i : Int) -> IntervalId {
  IntervalId(i)
}

pub fn IntervalId::hash(self : IntervalId) -> Int {
  self.0
}

let functions : @hashmap.HashMap[FnId, () -> Unit] = @hashmap.HashMap::[]

let timeout_ids : @hashmap.HashMap[TimeoutId, FnId] = @hashmap.HashMap::[]

let interval_ids : @hashmap.HashMap[IntervalId, FnId] = @hashmap.HashMap::[]

let fid : Ref[Int] = { val: 0 }

fn new_fid() -> FnId {
  let id = fid.val
  fid.val = fid.val + 1
  FnId(id)
}

pub fn fire(id : Int) -> Unit {
  // println("fire \(id)")
  match functions.get(FnId(id)) {
    Some(callback) => callback()
    None => println("function not found")
  }
}

pub fn set_timeout(cb : () -> Unit, ms : Int) -> TimeoutId {
  let fid = new_fid()
  println("set_timeout " + fid.0.to_string())
  functions.set(
    fid,
    fn() {
      cb()
      functions.remove(fid)
    },
  )
  let timeout_id = js_set_timeout(fid.0, ms)
  let tid = TimeoutId::new(timeout_id)
  timeout_ids.set(tid, fid)
  tid
}

pub fn clear_timeout(tid : TimeoutId) -> Unit {
  match timeout_ids.get(tid) {
    Some(fid) => {
      js_clear_timeout(tid.0)
      timeout_ids.remove(tid)
      functions.remove(fid)
      println("[mbt] timeout removed " + fid.0.to_string())
    }
    None => println("[mbt] timeout not found")
  }
}

pub fn set_interval(cb : () -> Unit, ms : Int) -> IntervalId {
  let fid = new_fid()
  // println("set_interval " + fid.0.to_string())
  functions.set(fid, cb)
  let id = js_set_interval(fid.0, ms)
  let iid = IntervalId::new(id)
  interval_ids.set(iid, fid)
  iid
}

pub fn clear_interval(id : IntervalId) -> Unit {
  match interval_ids.get(id) {
    Some(fid) => {
      js_clear_interval(id.0)
      interval_ids.remove(id)
      functions.remove(fid)
      println("[mbt] interval cleared" + fid.0.to_string())
    }
    None => println("[mbt] interval not found")
  }
}

pub fn run() -> Unit {
  let _t1 = set_timeout(
    fn() {
      println("[mbt] callback called")
      // xxx
    },
    100,
  )
  let t0 = set_timeout(
    fn() {
      println("[mbt] never called")
      // xxx
    },
    100,
  )
  clear_timeout(t0)
  let interval_id = set_interval(fn() { println("[mbt] interval called") }, 16)
  let _ = set_timeout(
    fn() {
      println("[mbt] clear interval")
      clear_interval(interval_id)
    },
    16 * 5,
  )

  // timout loop
  let mut cnt = 0
  let mut f : Option[() -> Unit] = None
  f = Some(
    fn() {
      let _ = set_timeout(
        fn() {
          cnt += 1
          // loop
          println("loop " + cnt.to_string())
          if cnt > 5 {
            f = None
            println("loop end")
            return ()
          }
          if f.is_empty().not() {
            let _ = set_timeout(f.unwrap(), 100)

          }
        },
        100,
      )

    },
  )
  f.unwrap()()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment