Home  >  Article  >  Backend Development  >  About Future/Promise in Golang

About Future/Promise in Golang

藏色散人
藏色散人forward
2021-04-15 14:33:522659browse

The following is the tutorial column of golang to introduce Future/Promise in Golang. I hope it will be helpful to friends in need!

About Future/Promise in Golang

Nowadays, the most common bottleneck in application execution is network requests. The network request only takes a few milliseconds, but the wait for the response takes a hundred times longer. So, if you perform multiple network requests, having them all execute in parallel is the best option to reduce latency. Future/Promise is one of the means to achieve this purpose.

A Future means that you need something "in the future" (usually the result of a network request), but you have to initiate such a request now, and the request will be executed asynchronously. Or to put it another way, you need to perform an asynchronous request in the background.

Future/Promise pattern has corresponding implementations in multiple languages. For example, ES2015 has Promise and async-await, Scala has built-in Future, and finally, Golang has goroutine and channel to achieve similar functions. A simple implementation is given below.

//RequestFuture, http request promise.
func RequestFuture(url string) <-chan []byte {
    c := make(chan []byte, 1)
    go func() {
        var body []byte
        defer func() {
            c <- body
        }()

        res, err := http.Get(url)
        if err != nil {
            return
        }
        defer res.Body.Close()

        body, _ = ioutil.ReadAll(res.Body)
    }()

    return c
}

func main() {
  future := RequestFuture("https://api.github.com/users/octocat/orgs")
  body := <-future
  log.Printf("reponse length: %d", len(body))
}

RequestFutureThe method returns a channel. At this time, the http request is still running asynchronously in a goroutine background. The main method can continue to execute other codes, such as triggering other Future, etc. When results are needed, we need to read the results from the channel. If the http request has not returned, the current goroutine will be blocked until the result is returned.

However, the above method still has some limitations. Error cannot be returned. In the above example, if an error occurs in the http request, the value of body will be nil/empty. However, since the channel can only return one value, you need to create a separate struct to wrap the two returned results.

Result after modification:

// RequestFutureV2 return value and error
func RequestFutureV2(url string) func() ([]byte, error) {
    var body []byte
    var err error

    c := make(chan struct{}, 1)
    go func() {
        defer close(c)

        var res *http.Response
        res, err = http.Get(url)
        if err != nil {
            return
        }

        defer res.Body.Close()
        body, err = ioutil.ReadAll(res.Body)
    }()

    return func() ([]byte, error) {
        <-c
        return body, err
    }
}

This method returns two results, solving the limitations of the first method. When used, it looks like this:

func main() {
    futureV2 := RequestFutureV2("https://api.github.com/users/octocat/orgs")

    // not block
    log.Printf("V2 is this locked again")

    bodyV2, err := futureV2() // block
    if err == nil {
        log.Printf("V2 response length %d\n", len(bodyV2))
    } else {
        log.Printf("V2 error is %v\n", err)
    }
}

The benefit of the above modification is that the futureV2() method can be called multiple times. And both can return the same result.

However, if you want to use this method to implement many different asynchronous functions, you need to write a lot of extra code. We can write a util method to overcome this difficulty.

// Future boilerplate method
func Future(f func() (interface{}, error)) func() (interface{}, error) {
    var result interface{}
    var err error

    c := make(chan struct{}, 1)
    go func() {
        defer close(c)
        result, err = f()
    }()

    return func() (interface{}, error) {
        <-c
        return result, err
    }
}

When calling the Future method, many channel tricks in the room will be executed. In order to achieve universal purposes, there is a type conversion from []buyte->interface{}->[]byte. If an error occurs, a runtime panic will be triggered.

The above is the detailed content of About Future/Promise in Golang. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete