chan bool如何让goroutine等待?

I'm building an app to run a command every time the code changes. I'm using a fsnotify for this feature. But, I can't understand how it is waiting a main goroutine.

I found that using sync.WaitGroup is more idiomatic, but I'm curious how done chan bool makes a goroutine is waiting in fsnotify example code.

I've tried to remove done in the example code of fsnotify, but it's not waiting a goroutine, just is exited.

watcher, err := fsnotify.NewWatcher()
if err != nil {
    log.Fatal(err)
}
defer watcher.Close()

done := make(chan bool)
go func() {
    for {
        select {
        case event, ok := <-watcher.Events:
            if !ok {
                return
            }
            log.Println("event:", event)
            if event.Op&fsnotify.Write == fsnotify.Write {
                log.Println("modified file:", event.Name)
            }
        case err, ok := <-watcher.Errors:
            if !ok {
                return
            }
            log.Println("error:", err)
        }
    }
}()

err = watcher.Add("/tmp/foo")
if err != nil {
    log.Fatal(err)
}
<-done

As per my study, I found an answer from one guy in reddit.com. This is kind of trick though, using <-done makes the main goroutine waiting an any value from chan done, eventually this app keeps running for fsnotify to watch and send a event to the main goroutine.

I'm not entirely sure what you're asking, but there's a subtle bug in the code you've provided.

A done channel is a common way to block until an action completes. It is used like this:

done := make(chan X) // Where X is any type
go func() {
    // Some logic, possibly in a loop
    close(done)
}()
// Other logic
<-done // Wait for `done` to be closed

The type of the channel is unimportant, as no data is (necissarily) sent over the channel, so bool works, but struct{} is more idiomatic, as it indicates that no data can be sent.

Your example almost does this, except that it never calls close(done). This is a bug. It means that the code will always wait forever at <-done, thus negating the entire purpose of a done channel. Your example code will never exit.

This means the code, as you have provided, could be also written as:

go func() {
    // Do stuff
}()
// Do other stuff
<Any code that blocks forever>

Because there are countless ways to block forever--none of them ever useful in practice--the channel in your example is not needed.