Async JS with Promises from Go

Async JS with Promises from Go

In JavaScript, Promise’s are the foundation of async/await.

Lets take up an example, consider the below code, This will create a Promise that resolves with a message after 3 seconds:

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("A happy hippo hopped and hiccupped")
    }, 3000)
})

async function, the await on the Promise above, so after 3 seconds you receive the message:

// This is an async function, which can contain "await" statements inside
async function MyFunc() {
    // Create the Promise
    const p = new Promise((resolve, reject) => {
        // After a 3 second timeout, this calls "resolve" with the message we're passing
        setTimeout(() => {
            resolve("A happy hippo hopped and hiccupped")
        }, 3000)
    })
    // Await for the Promise - this resolves after 3 seconds
    const message = await p
    console.log(message)
}

As per the documentation, we cannot make blocking calls in Go inside a function that is invoked by JavaScript directly — if we do that, we will get into a deadlock and the app will crash. Here is what the documentation recommends, all blocking calls be inside a goroutine, which raises the problem of then returning the value to the JavaScript code.

Quote from Documentation.

Invoking the wrapped Go function from JavaScript will pause the event loop and spawn a new goroutine. Other wrapped functions which are triggered during a call from Go to JavaScript get executed on the same goroutine.
As a consequence, if one wrapped function blocks, JavaScript’s event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.

Using a Promise is probably the best way to solve this problem: avoiding deadlocks whereas permitting programming with idiomatical JavaScript.We saw within the previous article that we are able to produce custom JavaScript objects from Go, and this is applicable to promises as well.We just need to create the Promise object by passing a function to the constructor. in a Pure-JS code , this function has two arguments, which are functions themselves, resolve should be invoked with the final result when the promise is completed. and when reject can be called when there is a an error to make the promise fail.

Here’s an updated MyGoFunc that resolves with a message after 3 seconds:

// MyGoFunc returns a Promise that resolves after 3 seconds with a message
func MyGoFunc() js.Func {
	return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Handler for the Promise: this is a JS function
// It receives two arguments, which are JS functions themselves: resolve and reject
		handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			resolve := args[0]
// Commented out because this Promise never fails
//reject := args[1]

			// Now that we have a way to return the response to JS, spawn a goroutine
// This way, we don't block the event loop and avoid a deadlock
			go func() {
// Block the goroutine for 3 seconds
				time.Sleep(3 * time.Second)
// Resolve the Promise, passing anything back to JavaScript
// This is done by invoking the "resolve" function passed to the handler
				resolve.Invoke("A happy hippo hopped and hiccupped")
			}()

// The handler of a Promise doesn't return any value
			return nil
		})

		// Create and return the Promise object
		promiseConstructor := js.Global().Get("Promise")
		return promiseConstructor.New(handler)
	})

Let's invoke this from a JavaScript function

async function MyFunc() {
    // Get the Promise from Go
    const p = MyGoFunc()
    // Show the current UNIX timestamps (in seconds)
    console.log(Math.floor(Date.now() / 1000))
    // Await for the Promise to resolve
    const message = await p
    // Show the current timestamp in seconds, then the result of the Promise
    console.log(Math.floor(Date.now() / 1000), message)
}

/*
Result:
  1604114580
  1604114580 "A happy hippo hopped and hiccupped"
*/

If your Go code errors, you can throw exceptions to JavaScript by using the reject function instead. For example:

// MyGoFunc returns a Promise that fails with an exception about 50% of times
func MyGoFunc() js.Func {
	return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Handler for the Promise
handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
	resolve := args[0]
	reject := args[1]

// Run this code asynchronously
	go func() {
// Cause a failure 50% of times
if rand.Int()%2 == 0 {
// Invoke the resolve function passing a plain JS object/dictionary
					resolve.Invoke(map[string]interface{}{
"message": "it worked!",
		"error":   nil,
		})
	} else {					
// Assume this were a Go error object
err := errors.New("it failed")
// Create a JS Error object and pass it to the reject function
// The constructor for Error accepts a string,
// so we need to get the error message as string from "err"
errorConstructor := js.Global().Get("Error")
errorObject := errorConstructor.New(err.Error())
reject.Invoke(errorObject)
		}
}()

// The handler of a Promise doesn't return any value
return nil
})

// Create and return the Promise object
	promiseConstructor := js.Global().Get("Promise")
	return promiseConstructor.New(handler)
	})
}

While we invoke this from JavaScript, we can see the returned object about half of the times, and we get an exception the other half. Note that we’re invoking the reject function with an actual JavaScript Error object, as best practice in JavaScript!

async function MyFunc() {
    try {
        console.log(await MyGoFunc())
    } catch (err) {
        console.error('Caught exception', err)
    }
}

/*
Result is either:
  {error: null, message: "it worked!"}
Or a caught exception (followed by the stack trace):
  Caught exception Error: Nope, it failed
*/


要查看或添加评论,请登录

Dr.MuthuKumaraswamy B的更多文章

  • AI Agents in Enterprise Production: Strategies for Success

    AI Agents in Enterprise Production: Strategies for Success

    AI and ML-driven agents are revolutionizing enterprise environments, but deploying them effectively in production…

  • Inside the Mind of an AI Agent: How It Works and Why It Matters

    Inside the Mind of an AI Agent: How It Works and Why It Matters

    Autonomous agents operate much like humans, relying on fundamental components such as identity, memory, planning, and…

  • Faster way to learn programming languages

    Faster way to learn programming languages

    Getting better at something faster isn't about skipping topics and being half-baked. I'm not showing you any shortcuts.

    3 条评论
  • Explaining AI: What Do Business Leaders Need to Care?

    Explaining AI: What Do Business Leaders Need to Care?

    AI and the challenge of model explainability. The use of artificial intelligence (AI) has become more widespread and is…

  • Vertex-AI

    Vertex-AI

    At the Google I/O developer conference earlier in May, Google Cloud announced the global launch of Vertex AI, a machine…

  • Multi-cloud redefined by Google

    Multi-cloud redefined by Google

    Ask anyone from the IT industry or anyone who observes the IT industry to name the top trends in the industry which…

    1 条评论
  • Axios Vs fetch what should you use

    Axios Vs fetch what should you use

    One of the basic tasks of any web application is to speak with servers through the Hypertext Transfer Protocol (HTTP)…

  • JavaScript Objects in Go and WebAssembly

    JavaScript Objects in Go and WebAssembly

    WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Implement your…

  • JavaScript Design Patterns

    JavaScript Design Patterns

    A pattern describes a Challange that happens over and another time in the environment, so describes the core of the…

  • Top 4 Reasons to adopt JAMstack

    Top 4 Reasons to adopt JAMstack

    JAM stands for JavaScript, APIs, and markup. Primarily you build the principles for the generator in JavaScript, use…

社区洞察

其他会员也浏览了