Creating waiting/busy indicator for executed proce

2019-02-25 11:24发布

i've program which execute child process like

cmd := exec.Command("npm", "install")
log.Printf("Running command and waiting for it to finish...")
err := cmd.Run()
log.Printf("Command finished with error: %v", err)

While this command is running it download and install npm packages which take some time between 10 to 40 sec, and the user doesnt know what happen until he see the stdout (10-40 sec depend on the network) , there is something that I can use which prints something to the cli to make it more clear that something happen, some busy indicator(any type) until the stdout is printed to the cli ?

2条回答
叼着烟拽天下
2楼-- · 2019-02-25 11:54

You may use another goroutine to print something (like a dot) periodically, like in every second. When the command completes, signal that goroutine to terminate.

Something like this:

func indicator(shutdownCh <-chan struct{}) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            fmt.Print(".")
        case <-shutdownCh:
            return
        }
    }
}

func main() {
    cmd := exec.Command("npm", "install")
    log.Printf("Running command and waiting for it to finish...")

    // Start indicator:
    shutdownCh := make(chan struct{})
    go indicator(shutdownCh)

    err := cmd.Run()

    close(shutdownCh) // Signal indicator() to terminate

    fmt.Println()
    log.Printf("Command finished with error: %v", err)
}

If you want to start a new line after every 5 dots, this is how it can be done:

func indicator(shutdownCh <-chan struct{}) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for i := 0; ; {
        select {
        case <-ticker.C:
            fmt.Print(".")
            if i++; i%5 == 0 {
                fmt.Println()
            }
        case <-shutdownCh:
            return
        }
    }
}
查看更多
Animai°情兽
3楼-- · 2019-02-25 12:09

Another way is to turn icza's answer around. Since the npm command is a long-running execution, it would probably be better to use a goroutine for it instead of the ticker (or both as a goroutine), but it's a matter of preference.

Like this:

func npmInstall(done chan struct{}) {
    cmd := exec.Command("npm", "install")
    log.Printf("Running command and waiting for it to finish...")

    err := cmd.Run()
    if err != nil {
        log.Printf("\nCommand finished with error: %v", err)
    }
    close(done)
}

func main() {
    done := make(chan struct{})
    go npmInstall(done)

    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            fmt.Print(".")
        case <-done:
            fmt.Println()
            return
        }
    }
}
查看更多
登录 后发表回答