Handling Errors in Golang

Handling Errors in Golang

Errors in golang are vastly different from errors in JavaScript. In this post, we will implement error handling for the events project we started building in the first post . That means, handling errors within the module itself as well as errors from event listeners.

First thing of note, with errors in golang is that errors are values not events. In JavaScript, errors (or Exceptions) are things that happen (events) that you need to catch and react to. In golang, errors are returned and you use them as values to make decisions. See this tutorial for more details on error handling.

Implementation

For the problem we are trying to solve ( see first post, we will have to implement a special error event with a single listener that will print out the error and exit the application; this listener will get overwritten if the user creates a listener for errors.

func (ee EventEmitter) emit(event string, args ...interface{}) {
    if event == "error" {
        if _, ok := ee.events[event]; ok == false {
            fmt.Println(args...)
            panic("no listener registered")
        }
    }

    if e, ok := ee.events[event]; ok {
        for _, listener := range e.listeners {
            _, err := listener(args)
            if err != nil {
                ee.emit("error", err)
            }
        }
    }
}

We have updated the emit method to cater for when the error event is emitted without a listener being registered. We have also modified the signature for listeners; included return types and allow listeners to take any types (not just strings as before). This means we can check for when listeners return errors and emit the error event.

Usage

First thing is to implement a listener for error events, this is because we don't want the application to exit whenever an error is returned from a listener.

var errorHandler = func(args ...interface{}) (interface{}, error) {
    errors := args[0].([]interface{})
    for _, error := range errors {
        fmt.Println("ERROR!", error)
    }
    return nil, nil
}

This listener needs to be registered within the server initialization method

func (server server) init() {
    // Register all listeners
    server.eventsEmitter.on("connect", handleConnection)
    server.eventsEmitter.on("connect", logMessage)
    server.eventsEmitter.on("data", logMessage)
    server.eventsEmitter.on("error", errorHandler)
    server.eventsEmitter.on("disconnect", handleDisonnection)
}

Next, to trigger the error event, we modify the handleDisconnection listener to always return an error, this will help us test the error event.

var handleDisonnection = func(args ...interface{}) (interface{}, error) {
    return nil, errors.New("failed to disconnect")
}

When you run this program, all the listeners are triggered as before. But, this time instead of the message for disconnection we get the message for error in disconnection

http/connection/string Connected successfully
2021/12/29 15:10:40 Connected successfully
2021/12/29 15:10:40 Message from user 1
2021/12/29 15:10:40 Message from user 2
2021/12/29 15:10:40 Message from user 3
2021/12/29 15:10:40 Message from user 4
ERROR! failed to disconnect

Note that we have modified all listeners to follow the new signatures

Next Steps

We still have some unresolved issues with this library, I'll be updating the Github project with improvements as I think of them.

Did you find this article valuable?

Support Iroegbu Iroegbu by becoming a sponsor. Any amount is appreciated!