Home >Backend Development >Golang >How to return from function when timer completes in go?
I just started developing a toy test cli program. I'm trying to implement a timer so that when the timer ends, the quiz is over.
This is my initial implementation.
func StartTimer(quizFinished chan bool, timer *time.Timer) { // Start timer <-timer.C fmt.Println("\nQuiz has ended") quizFinished <- true return } func askQuestions(timer *time.Timer, questions []string, answers []string) []string { var input string var userAnswers []string quizFinished := make(chan bool) fmt.Println("Starting Quiz...") go StartTimer(quizFinished, timer) for index, element := range questions { select { case <-quizFinished: fmt.Println("Quiz has ended") return userAnswers default: fmt.Printf("Question %d: %s? ", index+1, element) fmt.Scanln(&input) userAnswers = append(userAnswers, input) } } return userAnswers }
Edit implementation
package main import ( "bufio" "flag" "fmt" "log" "os" s "strings" "time" ) func readFileData(fileName string) (questions []string, answers []string) { file, err := os.Open(fileName) if err != nil { log.Fatalf("failed reading data from file: %v", err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { questionAndAnswer := s.Split(scanner.Text(), ",") question, answer := questionAndAnswer[0], questionAndAnswer[1] questions = append(questions, question) answers = append(answers, answer) } return questions, answers } func askQuestions(timer *time.Timer, questions []string) []string { var input string var userAnswers []string = make([]string, 0, len(questions)) quizFinished := make(chan bool, 1) fmt.Println("Starting Quiz...") go func() { <-timer.C fmt.Println("\nquiz over...") quizFinished <- true }() for index, element := range questions { fmt.Printf("Question %d: %s? ", index+1, element) answerCh := make(chan string, 1) go func() { fmt.Scanln(&input) answerCh <- input }() select { case <-quizFinished: return userAnswers case answer := <-answerCh: userAnswers = append(userAnswers, answer) } } return userAnswers } func main() { fileNamePtr := flag.String("filename", "problems.csv", "Specify a file name to read in") quizTimePtr := flag.Int("time", 5, "Specify how long the quiz should take") flag.Parse() timer := time.NewTimer(time.Duration(*quizTimePtr) * time.Second) questions, answers := readFileData(*fileNamePtr) // var userAnswers = askQuestions(timer, questions, answers) var userAnswers = askQuestions(timer, questions) correctAnswers := 0 incorrectAnswers := 0 for i := 0; i < len(answers); i++ { if userAnswers[i] == answers[i] { correctAnswers++ } else { incorrectAnswers++ } } grade := float32(correctAnswers) / float32(len((questions))) fmt.Printf("Total Questions: %d \n", len(questions)) fmt.Printf("Total Correct Answers: %d \n", correctAnswers) fmt.Printf("Total Incorrect Answers: %d \n", incorrectAnswers) fmt.Printf("Grade: %.0f%% \n", grade*100) }
I keep getting the following output and error:
Starting Quiz... Question 1: 5+5? 10 Question 2: 1+1? Quiz has ended 2 Returning from quiz... panic: runtime error: index out of range [2] with length 2 goroutine 1 [running]:
I don't understand channels and goroutines well enough yet to understand what's going on. The expected behavior is that when the timer stops, it prints "Quiz ended" and sends a value to the quizFinished channel, which is read in the select statement. Another problem I have is that Scanln will wait for user input after the quiz is over. Any help would be greatly appreciated!
When you call the Scanln
function in the main goroutine, it pauses and waits for user input. Even if your timer completes in another Goroutine and tries to notify the main Goroutine, the main Goroutine will not respond because it is blocked by the "Scanner".
That's why even after the timer expires, Scanln
is still waiting for user input.
package main import ( "fmt" "time" ) func askQuestions(timer *time.Timer, questions []string) []string { var input string var userAnswers []string quizFinished := make(chan bool, 1) fmt.Println("start quiz...") go func() { <-timer.C fmt.Println("\nquiz over...") quizFinished <- true }() for index, element := range questions { fmt.Printf("question %d: %s? ", index+1, element) answerCh := make(chan string, 1) go func() { fmt.Scanln(&input) answerCh <- input }() select { case <-quizFinished: fmt.Println("exit quiz...") return userAnswers case answer := <-answerCh: userAnswers = append(userAnswers, answer) } } return userAnswers } func main() { timer := time.NewTimer(10 * time.Second) questions := []string{"5+5", "1+1", "8+3"} responses := askQuestions(timer, questions) fmt.Println("answer:", responses) }
The above is the detailed content of How to return from function when timer completes in go?. For more information, please follow other related articles on the PHP Chinese website!