diff --git a/globals.ts b/globals.ts index f25cc710b1..d38fbf47d2 100644 --- a/globals.ts +++ b/globals.ts @@ -1,4 +1,4 @@ -import { setTimeout } from "./timers"; +import * as timer from "./timers"; // If you use the eval function indirectly, by invoking it via a reference // other than eval, as of ECMAScript 5 it works in the global scope rather than @@ -14,7 +14,10 @@ export const _global = globalEval("this"); _global["window"] = _global; // Create a window object. import "./url"; -_global["setTimeout"] = setTimeout; +_global["setTimeout"] = timer.setTimeout; +_global["setInterval"] = timer.setInterval; +_global["clearTimeout"] = timer.clearTimer; +_global["clearInterval"] = timer.clearTimer; const print = V8Worker2.print; diff --git a/msg.proto b/msg.proto index 081b2df8cb..68ff52255d 100644 --- a/msg.proto +++ b/msg.proto @@ -16,6 +16,7 @@ message Msg { ExitMsg exit = 14; TimerStartMsg timer_start = 15; TimerReadyMsg timer_ready = 16; + TimerClearMsg timer_clear = 17; } } @@ -60,3 +61,5 @@ message TimerReadyMsg { optional int32 id = 1; optional bool done = 2; } + +message TimerClearMsg { optional int32 id = 1; } diff --git a/testdata/004_set_timeout.ts b/testdata/004_set_timeout.ts index fe55e31ba0..cc55bf76f7 100644 --- a/testdata/004_set_timeout.ts +++ b/testdata/004_set_timeout.ts @@ -3,3 +3,9 @@ setTimeout(function() { }, 10); console.log("Hello"); + +const id = setTimeout(function() { + console.log("Not printed"); +}, 10000); + +clearTimeout(id); diff --git a/testdata/010_set_interval.ts b/testdata/010_set_interval.ts new file mode 100644 index 0000000000..e013d00bc6 --- /dev/null +++ b/testdata/010_set_interval.ts @@ -0,0 +1,7 @@ +const id = setInterval(function() { + console.log("test") +}, 200); + +setTimeout(function() { + clearInterval(id) +}, 500) diff --git a/testdata/010_set_interval.ts.out b/testdata/010_set_interval.ts.out new file mode 100644 index 0000000000..dec2cbe1fa --- /dev/null +++ b/testdata/010_set_interval.ts.out @@ -0,0 +1,2 @@ +test +test diff --git a/timers.go b/timers.go index f9fe28608e..6a395adaca 100644 --- a/timers.go +++ b/timers.go @@ -5,6 +5,16 @@ import ( "time" ) +type Timer struct { + Id int32 + Done bool + Cleared bool + Interval bool + Duration int32 // In milliseconds +} + +var timers = make(map[int32]*Timer) + func InitTimers() { Sub("timers", func(buf []byte) []byte { msg := &Msg{} @@ -12,28 +22,62 @@ func InitTimers() { switch msg.Payload.(type) { case *Msg_TimerStart: payload := msg.GetTimerStart() - return HandleTimerStart(*payload.Id, *payload.Interval, - *payload.Duration) + timers[*payload.Id] = &Timer{ + Id: *payload.Id, + Done: false, + Interval: *payload.Interval, + Duration: *payload.Duration, + Cleared: false, + } + timers[*payload.Id].StartTimer() + return nil + case *Msg_TimerClear: + payload := msg.GetTimerClear() + // TODO maybe need mutex here. + timer := timers[*payload.Id] + timer.Clear() + return nil default: panic("[timers] Unexpected message " + string(buf)) } }) } -func HandleTimerStart(id int32, interval bool, duration int32) []byte { +func (t *Timer) Clear() { + if !t.Cleared { + wg.Done() + t.Cleared = true + delete(timers, t.Id) + } + t.Done = true +} + +func (t *Timer) StartTimer() { wg.Add(1) go func() { - defer wg.Done() - time.Sleep(time.Duration(duration) * time.Millisecond) - payload, err := proto.Marshal(&Msg{ - Payload: &Msg_TimerReady{ - TimerReady: &TimerReadyMsg{ - Id: &id, + defer t.Clear() + for { + time.Sleep(time.Duration(t.Duration) * time.Millisecond) + if !t.Interval { + t.Done = true + } + pubMsg(&Msg{ + Payload: &Msg_TimerReady{ + TimerReady: &TimerReadyMsg{ + Id: &t.Id, + Done: &t.Done, + }, }, - }, - }) - check(err) - Pub("timers", payload) + }) + if t.Done { + return + } + } }() - return nil +} + +func pubMsg(msg *Msg) { + payload, err := proto.Marshal(msg) + check(err) + Pub("timers", payload) } diff --git a/timers.ts b/timers.ts index f5f039f1cd..5407c63b00 100644 --- a/timers.ts +++ b/timers.ts @@ -4,12 +4,14 @@ import * as dispatch from "./dispatch"; let nextTimerId = 1; // tslint:disable-next-line:no-any -type TimerCallback = (...args: any[]) => void; +export type TimerCallback = (...args: any[]) => void; interface Timer { id: number; cb: TimerCallback; interval: boolean; + // tslint:disable-next-line:no-any + args: any[]; duration: number; // milliseconds } @@ -23,17 +25,26 @@ function onMessage(payload: Uint8Array) { const msg = pb.Msg.decode(payload); const { id, done } = msg.timerReady; const timer = timers.get(id); - timer.cb(); + if (!timer) { + return; + } + timer.cb(...timer.args); if (done) { timers.delete(id); } } -export function setTimeout(cb: TimerCallback, duration: number): number { +export function setTimeout( + cb: TimerCallback, + duration: number, + // tslint:disable-next-line:no-any + ...args: any[] +): number { const timer = { id: nextTimerId++, interval: false, duration, + args, cb }; timers.set(timer.id, timer); @@ -46,3 +57,34 @@ export function setTimeout(cb: TimerCallback, duration: number): number { }); return timer.id; } + +// TODO DRY with setTimeout +export function setInterval( + cb: TimerCallback, + repeat: number, + // tslint:disable-next-line:no-any + ...args: any[] +): number { + const timer = { + id: nextTimerId++, + interval: true, + duration: repeat, + args, + cb + }; + timers.set(timer.id, timer); + dispatch.sendMsg("timers", { + timerStart: { + id: timer.id, + interval: true, + duration: repeat + } + }); + return timer.id; +} + +export function clearTimer(id: number) { + dispatch.sendMsg("timers", { + timerClear: { id } + }); +}