同时调用许多服务的模式每个都返回值和错误

I'm still experiencing how to make best use of channels. I have 5 outbound service calls (take ~2 min to return), and each gives me a pair of return values. e.g. func serviceCall()(T, error)

I want to make them concurrent, but I find the code very lengthy.

Basically, I have to create 5 channels, 5 structs to hold the return value.

I wrote a simple example to express the scenario, I want to know what is the pattern for this scenario, how I can make this code better.

package main

import (
    "fmt"
    "math/rand"
    "time"
    "log"
)

// goal: run multiple functions concurrently and process the results.
func main() {
now := time.Now()
    // method1
    type res1 struct {
        news string
        err  error
    }
    type res2 struct {
        n   int
        err error
    }

    ch1 := make(chan *res1)
    ch2 := make(chan *res2)

    go func() {
        var res res1
        res.news, res.err = news1()
        ch1 <- &res
    }()

    go func() {
        var res res2
        res.n, res.err = news2()
        ch2 <- &res
    }()

    one := <-ch1
    if one.err != nil {
        log.Fatal(one.err)
    }
    fmt.Println("news1: ", one.news)

    two := <-ch2
    if two.err != nil {
        log.Fatal(two.err)
    }
    fmt.Println("news2: ", two.n)

    fmt.Println("time elapsed: ", time.Since(now))

}

// first sleeps 5 seconds and returns random number or error.
func news1() (string, error) {
    time.Sleep(time.Second * 5)
    return "new1 is here.", nil
}

// second sleeps random seconds and returns random number or error.
func news2() (int, error) {
    n := rand.Intn(20)
    time.Sleep(time.Duration(n) * time.Second)

    return n, nil
}

As far as I can tell your requirement of 5 service calls outbound is not reflected in the example code that you have written. The example code seems like it will work (I didn't run it to check) but it doesn't cover your use case.

I would recommend starting smaller with goroutines. This site and linked youtube talk helped me get started understanding how to manage goroutines.

https://talks.golang.org/2012/concurrency.slide#1

This video also helped me immensely when I started getting lost in the above talk:

https://www.youtube.com/watch?v=LvgVSSpwND8

There is no single pattern for this. There are different ways to accomplish it. The simplest is probably with a waitgroup, which requires no channels at all. The pattern for this would look like this:

func dostuff() {
    var result1 int
    var result2 string
    var resultN SomeStruct
    var err1, err2, errN error

    wg := sync.WaitGroup{}

    wg.Add(1)
    go func() {
        defer wg.Done()
        result1, err1 = doStuff1()
    }

    wg.Add(1)
    go func() {
        defer wg.Done()
        result2, err2 = doStuff2()
    }

    // repeat as often as you like

    wg.Add(1)
    go func() {
        defer wg.Done()
        resultN, errN = doStuffN()
    }

    wg.Wait()
    // handle results and errors
}

The most obvious shortcoming of this approach is that you don't have the flexibility to abort any outstanding operations in case of an error. This may or may not matter to you.