Search code examples
goslackslack-api

How to make my function return to Slack right away?


I'm trying to develop a simple API for Slack and I want to return something to the user right away to avoid the 3000 ms timeout.

Here are my questions:

  1. Why the This should be printed to Slack first doesn't get printed right away, instead I only got the last message which is The long and blocking process completed? But it appears in ngrok log though.

  2. Why is my function still reaching the 3000 ms limit even though I'm already using a go routine? Is it because of the done channel?

func testFunc(w http.ResponseWriter, r *http.Request) {
    // Return to the user ASAP to avoid 3000ms timeout.
    // But this doesn't work. Nothing is returned but
    // the message appeared in ngrok log.
    fmt.Fprintln(w, "This should be printed to Slack first!")

    // Get the response URL.
    r.ParseForm()
    responseURL := r.FormValue("response_url")

    done := make(chan bool)

    go func() {
        fmt.Println("Warning! This is a long and blocking process.")
        time.Sleep(5 * time.Second)

        done <- true
    }()

    // This works! I received this message. But I still reached the 3000ms timeout.
    func(d bool) {
        if d == true {
            payload := map[string]string{"text": "The long and blocking process completed!"}
            j, err := json.Marshal(payload)

            if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
            }

            http.Post(responseURL, "application/json", bytes.NewBuffer(j))
        }
    }(<-done)
}

Solution

  • http.ResponseWriter streams are buffered by default. If you want data to be sent to a client in realtime (e.g. HTTP SSE), you need to flush the stream after each 'event':

    wf, ok := w.(http.Flusher)
    
    if !ok {
        http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
        return
    }
    
    fmt.Fprintln(w, "This should be printed to Slack first!")
    
    wf.Flush()
    

    Flushing is expensive - so take advantage of go's buffering. There will always be an implicit flush once your handler finally exits (hence why you saw your output 'late').