遍历go例程时出现数组索引错误

I am running go routines in a loop for two functions using sync to wait until go routines are finished and then running a normal function outside the loop like:

func fetchStudentsAndTeachers(db *sqlx.DB, token string) {
    var students Students
    var teachers Teachers
    wg := &sync.WaitGroup{}
    // defer wg.Wait()
    tch := make(chan Teachers)
    schoolList := fetchActiveOrganization(DB)
    std := make(chan Students)
    for key, value := range schoolList {
        value2 := value
        fmt.Println(key, ":", value)
        wg.Add(1)
        go func() {
            defer wg.Done()
            std <- fetchStudentsFromSchool(wg, value2.CleverSchoolID, token)
        }()
        wg.Add(1)
        go func() {
            defer wg.Done()
            tch <- fetchTeachersFromSchool(wg, value2.CleverSchoolID, token)
        }()
        students = <-std
        // teachers = <-tch
    }
    wg.Wait() // It panics after this point
    UpdateOrganizationsAndUsers(DB)
    close(std)
    close(tch)
    fmt.Println(students)
    fmt.Println(teachers)
}

Now problem is when I come out of loop is is giving me index out of bound error. I checked is using delve debugger on wg.Wait() After forwarding the control of debugger from wg.Wait(). It panics saying:

panic: runtime error: index out of range

(Edited) Note: This problem is due to the loop iterates one time and run the routine which process databases. But somehow the loop iterate again before the routines completes which is causing the error. What should I do to complete both routines before next iteration.

If you want the 2 worker goroutines to finish before the next iteration starts, simply move the wg.Wait() call to the end of the loop body:

for key, value := range schoolList {
    value2 := value
    fmt.Println(key, ":", value)
    wg.Add(1)
    go func() {
        defer wg.Done()
        std <- fetchStudentsFromSchool(wg, value2.CleverSchoolID, token)
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        tch <- fetchTeachersFromSchool(wg, value2.CleverSchoolID, token)
    }()
    students = <-std
    teachers = <-tch
    wg.Wait()
}

Also note that if you're already using channels to deliver the goroutine results, and if no one else uses the std and tch channels, WaitGroup is not even needed:

for key, value := range schoolList {
    value2 := value
    fmt.Println(key, ":", value)
    go func() {
        std <- fetchStudentsFromSchool(wg, value2.CleverSchoolID, token)
    }()
    go func() {
        tch <- fetchTeachersFromSchool(wg, value2.CleverSchoolID, token)
    }()
    students = <-std
    teachers = <-tch
}

This is enough because the next iteration can only start if both receiving from std and tch are completed, but those can only happen if the worker goroutines do their job and send the result on these channels.

Now if we think about what this does: the loop's goroutine waits for 2 worker goroutines to finish their work and then proceed (to the next iteration). While the 2 workers work, it just waits.

We can simplify and improve this by doing 1 worker's job in the loop's goroutine, and when done, wait for the single worker to also finish (if not yet finished).

This is how it could look like:

for key, value := range schoolList {
    value2 := value
    fmt.Println(key, ":", value)
    go func() {
        std <- fetchStudentsFromSchool(wg, value2.CleverSchoolID, token)
    }()
    teachers = fetchTeachersFromSchool(wg, value2.CleverSchoolID, token)
    students = <-std
}

We simply fetch teachers in the loop's goroutine, and only fetch students in a concurrent goroutine. This has the same effect (students and teachers are fetched concurrently), with less overhead and cleaner code.

Also note that since you now have synchronization to not start the next iteration until the workers finish, you do not need to make a copy of the loop variable: it will not be modified during the workers' lifetime. So you may simply use:

for key, value := range schoolList {
    fmt.Println(key, ":", value)
    go func() {
        std <- fetchStudentsFromSchool(wg, value.CleverSchoolID, token)
    }()
    teachers = fetchTeachersFromSchool(wg, value.CleverSchoolID, token)
    students = <-std
}

(This applies to the solution with waitgroup too.)