About Future/Promise in Golang

藏色散人
Release: 2021-04-15 14:33:52
forward
2722 people have browsed it

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))
}
Copy after login

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
    }
}
Copy after login

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)
    }
}
Copy after login

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
    }
}
Copy after login

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!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template