DEV Community

Cover image for Concurrency comparison Javascript to GO
Jeff Adams
Jeff Adams

Posted on • Originally published at tenmilesquare.com

Concurrency comparison Javascript to GO

A lot has been written on concurrency and it is a deep subject. In order to understand the subtlety of how concurrency is implemented in Go and Javascript respectively, consider this concept through this high-level, relatively simple example.

For deeper understanding of how coroutines, channels, and generators work I will include some links at the bottom.

Example

Simple Async / Await for a http request. The goal is to wait until we have the data, but not block the main execution context while we wait.

Javascript

const axios = require('axios');

(async function() {
    console.log('here')
    const str = await axios.get("https://google.com")
    console.log('there')
    console.log(str.status)
    console.log('everywhere')
})()

console.log('also here')
Enter fullscreen mode Exit fullscreen mode

With output:

here
also here
there
200
everywhere
Enter fullscreen mode Exit fullscreen mode

Go

package main

import (
    "fmt"
    "net/http"
)

func get(url string, c chan int) {
    resp, _ := http.Get(url)
    fmt.Println("there")
    c <- resp.StatusCode
}

func main() {
    c := make(chan int)
    fmt.Println("here")
    go get("https://google.com", c)
    status := <-c
    fmt.Println("everywhere")
    fmt.Println(status)
}
Enter fullscreen mode Exit fullscreen mode

with output

here
there
everywhere
200
Enter fullscreen mode Exit fullscreen mode

In both of these examples, the main execution thread is not blocked.

Let's explore what that means.

In javascript, we see the rest of the program executing while the http request is being made. Note the "also here" log order. With the await we are avoiding callback hell by blocking the current function execution without blocking the overall process execution.

In Go, everything in the current context stops while we wait for the http request. This is because the main function waits for the return from our channel. But the thread is still available to the program as a whole see this explanation under "Blocking is fine". This is a result of the Go runtime and the core scheduler. It's very subtle the way that the main thread is blocked, but this is expected and encouraged behavior in Go because there is a top level language construct to schedule multiple processes in multiple threads and intelligently manage suspended contexts along the way.

We can also modify this example to show how execute other code while waiting for the http call to return.

package main

import (
    "fmt"
    "net/http"
    "time"
)

func get(url string, c chan int) {
    resp, _ := http.Get(url)
    fmt.Println("Everywhere")
    c <- resp.StatusCode
}

func main() {
    fmt.Println("here")
    c := make(chan int)
    go get("https://google.com", c)
    fmt.Println("there")
    for {
        select {
        case status := <-c:
            fmt.Println(status)
            return
        default:
            fmt.Println("Also Here")
            time.Sleep(1000 * time.Millisecond)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With Output:

here
Also Here
there
200
everywhere
Enter fullscreen mode Exit fullscreen mode

In this case, we loop in the main function using for { select {.... The main function will hit the default case over and over while waiting for the return value from our spawned channel. We can spawn other coroutines and add to the cases in the select, or write additional procedural code in the default case. In Go this is an expected pattern to allow the language scheduler to handle these routines and potentially multiple threads along the way.

A note on resource limitations.

Remember that Javascript is single threaded. Concurrency is based on coroutines. Go is multi threaded, but a coroutine does not always mean a new thread. See here

Conclusion

Concurrency is important, and coroutines make concurrency possible on fewer resources. Javascript and Go both have schemes to make concurrent, non-blocking, executions more readable and tolerable. The differences are subtle, but result in two tools with a robust ability to execute a lot of async calls on fewer resources.

Reading List

Top comments (0)