For the special case of cancelling timers, that is futures returned by 
`sleepAsync`, I have an implementation. It is a bit hacky though as it 
manipulates the dispatcher's timer list:
    
    
    proc cancelTimer*(timer: Future[void], error: ref CatchableError) =
      let dispatcher = getGlobalDispatcher()
      if dispatcher.timers.len() > 0:
        for i in 0 ..< dispatcher.timers.len():
          if dispatcher.timers[i].fut == timer:
            dispatcher.timers[i].fut.fail(error)
            dispatcher.timers.del(i)
            return
    
    
    Run

With this, I would implement your ticker example like this:
    
    
    import std/[asyncdispatch, times, asyncfutures]
    import std/[strutils, strformat]
    import std/heapqueue
    
    type Canceled = object of CatchableError
    
    type Ticker = ref object
      ident: int
      stop: int
      interval: int
      timer: Future[void]
      canceled: bool
    
    proc cancelTimer*(timer: Future[void], error: ref CatchableError) =
      let dispatcher = getGlobalDispatcher()
      if dispatcher.timers.len() > 0:
        for i in 0 ..< dispatcher.timers.len():
          if dispatcher.timers[i].fut == timer:
            dispatcher.timers[i].fut.fail(error)
            dispatcher.timers.del(i)
            return
    
    proc start(ticker: Ticker) {.async.} =
      var remain = ticker.stop
      while remain > 0:
        if ticker.canceled:
          break
        echo &"tick#{ticker.ident}: ", now()
        dec remain
        ticker.timer = sleepAsync(ticker.interval)
        try:
          await ticker.timer
        except Canceled:
          break
      echo &"tick#{ticker.ident}: exiting"
    
    proc cancel(ticker: Ticker) =
      cancelTimer(ticker.timer, newException(Canceled, "timer was canceled"))
      ticker.canceled = true
    
    proc cancelAfter(ticker: Ticker, timeout: int) {.async.} =
      await sleepAsync(timeout)
      ticker.cancel()
    
    proc run(ticker1, ticker2: Ticker) {.async.} =
      asyncCheck ticker1.cancelAfter(1000)
      await ticker1.start()
      asyncCheck ticker2.cancelAfter(5000)
      await ticker2.start()
    
    proc main: int =
      asyncCheck run(Ticker(ident: 1, stop: 3, interval: 500, canceled: false),
                     Ticker(ident: 2, stop: 3, interval: 500, canceled: false))
      while hasPendingOperations():
        poll(high(int))
    
    when isMainModule:
      quit main()
    
    
    Run

Reply via email to