How to read a key in Go but continue application if no key pressed within x seconds?

醉酒当歌 提交于 2020-01-17 06:52:10

问题


This is an easy way to read a key from the console

reader := bufio.NewReader(os.Stdin)

// ...

func readKey() rune {
    char, _, err := reader.ReadRune()
    if err != nil {
        fmt.Println("Error reading key: ", err)
    }
    return char
}

// ...

fmt.Println("Checking keyboard input...")    

loop:
for {
    keyb := readKey()
    switch keyb {
    case 'x':
      fmt.Println("x key pressed, exiting loop")
      break loop
    }
}

However the issue is the application always waits for a key to be read. What if you want to wait only 5 seconds for a key to be read, and if no key is read, continue the application?

I'm thinking that I must pull in a dependency maybe, such as ncurses or a unit (module) that turbopascal had which was called crt and had a readkey function. But is a dependency really necessary or is there an easy way to do it without? Possibly even some defer() tricks, I don't know.


回答1:


You don't need external dependencies to achieve this.

You can use a channel and set a timeout on it.

Here's documentation info about that: https://gobyexample.com/timeouts

The key part is making the input go through the channel in a separate goroutine, so that the main thread does not block waiting. You can then decide how long to wait to receive the input through the channel by setting a timeout in the select clause.

And here's a working sample using your post as a base:

package main 

import (
    "bufio"
    "os"
    "log"
    "fmt"
    "time"
)

var reader = bufio.NewReader(os.Stdin)

func readKey(input chan rune) {
    char, _, err := reader.ReadRune()
    if err != nil {
        log.Fatal(err)
    }
    input <- char
}

func main() {
    input := make(chan rune, 1)
    fmt.Println("Checking keyboard input...")
    go readKey(input)
    select {
        case i := <-input:
            fmt.Printf("Input : %v\n", i)
        case <-time.After(5000 * time.Millisecond):
            fmt.Println("Time out!")
    }
}



回答2:


Probably the most "go-isch" way to do this is using a goroutine and channels. You start two goroutines, one which waits for input, and one which sleeps until after the timeout period. You then use a select statement in your main goroutine to check what event happened first (the input, or the timeout). Example code:

package main

import (
    "fmt"
    "time"
)

func waitForInput(didInput chan<- bool) {
    // Wait for a valid input here

    didInput <- true
}

func main() {
    didInput := make(chan bool, 1)
    timeout := make(chan bool, 1)

    go func() {
        time.Sleep(5 * time.Second)
        timeout <- true
    }()

    go waitForInput(didInput)

    select {
    case <-didInput:
        fmt.Println("")
        // Continue your application here
    case <-timeout:
        // Input timed out, quit your application here
    }
}


来源:https://stackoverflow.com/questions/43965556/how-to-read-a-key-in-go-but-continue-application-if-no-key-pressed-within-x-seco

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!