使用 ctrl+c 退出的带有时间指示器的及时轮询控制台应用程序

Timely polling console app with time indicator that exits on ctrl+c

我想及时汇集代币。令牌本身也获得了有关何时过期的信息。 这应该 运行 永远,直到用户输入 ctrl+c。

我用

做了同样的尝试
span := timeLeft(*expDate)
timer := time.NewTimer(span).C
ticker := time.NewTicker(time.Second * 5).C

这也不起作用(应用程序在倒计时后挂起)。所以我决定尝试 <- time.After(...)

我的代码不起作用。您会看到倒计时,但它不会在过期时中断。

为了简单起见,这是一个带有轮询逻辑的小摘录 main.go:

func refreshToken() (time.Time, error) {
        //This should simulate a http request and returns the new target date for the next refresh
        time.Sleep(2 * time.Second)
        return time.Now().Add(10 * time.Second), nil
    }

func timeLeft(d time.Time) time.Duration {
    exactLeft := d.Sub(time.Now())
    floorSeconds := math.Floor(exactLeft.Seconds())
    return time.Duration(floorSeconds) * time.Second
}

func poller(expDate *time.Time) {
    exp := timeLeft(*expDate)

    done := make(chan bool)
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    for {
        select {

        // print time left on the screen
        case <-time.After(3 * time.Second):
            go func() {
                fmt.Printf("\rNext Token refresh will be in: %v", timeLeft(*expDate))
            }()
        // mark as done when date is due
        case <-time.After(exp):
            fmt.Println("Refresh token now!")
            done <- true

        // exit app
        case <-c:
            os.Exit(0)
            break

        // exit function when done
        case <-done:
            break
        }

    }
}

func main() {

    var expiration time.Time
    expiration = time.Now().Add(10 * time.Second)
    // loop and refresh token as long as the app does not exit
    for {
        poller(&expiration)

        ex, err := refreshToken()
        expiration = ex
        if err != nil {
            panic(err)
        }

        fmt.Println("next round poller")
    }
}

我也不确定我是否需要 done 频道? 监听两个定时器并调用自身直到有人按下 ctrl+c 需要什么?

找到解决办法。虽然 @ain 对缓冲完成通道的理解是正确的,但现在代码中并不真正需要它。没有它也能工作。

这个技巧确实在 for 循环之外有 timer,在循环内部有 ticker。原因是 time.After 是一个 func 那 return 每次迭代都是一个新频道。这个接缝对于自动收报机来说非常好,但对于 timer 就不行了。 通过以下更改,它起作用了 =) ...

   func poller(expDate *time.Time) {
        exp := timeLeft(*expDate)
        timer := time.After(exp)

        fmt.Printf("Next Token refresh will be in: %v\n", exp)

        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt)
        for {
            select {

            // print time left on the screen
            case <-time.After(3 * time.Second):
                go func() {
                    fmt.Printf("\r     ")
                    fmt.Printf("\r%v", timeLeft(*expDate))
                }()
            // mark as done when date is due
            case <-timer:
                fmt.Println("Refresh token now!")
                return

            // exit app
            case <-c:
                os.Exit(0)
                break

            }
        }
    }