DEV Community

Cover image for Error Handling in JavaScript (Golang Style)
Bibek
Bibek

Posted on • Originally published at blog.bibekkakati.me

Error Handling in JavaScript (Golang Style)

In this short article, we are going to see how we can handle error in JavaScript in Golang style.

I am assuming, you have some experience with JavaScript and you are aware of the issues with error handling like throwing an exception to the parent method from try-catch block or chaining multiple then-blocks and implementing logic inside it. These things can easily mess up with the code making it difficult to read.

Golang avoids these type of problems by handling errors/exceptions atomically.

Error handling in Golang



result, err := methodCall()
if err != nil {
  // handle error
}
// do something with the result


Enter fullscreen mode Exit fullscreen mode

We can use a similar pattern in JavaScript with the help of a try-catch block like this.



const getData = async () => {
  try {
    const result = await methodCall();
    return [result, null];
  } catch(error) {
    return [null, error];
  }
}


Enter fullscreen mode Exit fullscreen mode

If any error occurs, we are returning the error in the second position of the array and the result as null in the first position.

If there is no error, we are returning the result in the first position and error as null in the second position.

Now we can call the getData method then handle the result and error like this.



const [result, error] = await getData();
if (error !== null) {
  // handle the error
}
// do something with the result


Enter fullscreen mode Exit fullscreen mode

This pattern of error handling makes it very easy to read and understand the code.

Let me know what do you think about this pattern.


Originally published on blog.bibekkakati.me


Thank you for reading 🙏

If you enjoyed this article or found it helpful, give it a thumbs-up 👍

Feel free to connect 👋

Twitter | Instagram | LinkedIn


If you like my work and want to support it, you can do it here. I will really appreciate it.



Top comments (31)

Collapse
 
drumstickz64 profile image
Drumstickz64 • Edited

You can actually turn this into a utility function, like so:

async function goCatch(promise) {
    try {
        const result = await promise
        return [result, null]
    } catch (err) {
        return [null, err]
    }
}
Enter fullscreen mode Exit fullscreen mode

You could also use an object like Brendan said.

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I would wrap it in a higher order function though, something that takes an async function and returns a new async function with the added catch logic

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️ • Edited

Something like this, I guess:

const safe = fn => (*args) => fn(*args)
   .then(res => [res, null])
   .catch(err => [null, err])
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bibekkakati profile image
Bibek

Yeah. That can be way of implementing it.

Collapse
 
bibekkakati profile image
Bibek • Edited

Yeah. Utility function can be very helpful.

Collapse
 
douglascalora profile image
DouglasCalora • Edited
import { Loading, Notify } from 'quasar'

/**
 * @param {Promise} promise
 * @param {object} config={notifySuccess, notifyError, useLoading}
 * @example promiseHandle(new Promise(), { notifyError: { message: 'Erro' }, notifySuccess: { message: 'Sucesso' } })
 */
export default async function (promise, config = {}) {
  const {
    notifySuccess = {},
    notifyError = {},
    useLoading = false,
    onLoading
  } = config

  onLoading && onLoading(true)
  useLoading && Loading.show()

  const primiseToBeExec = typeof promise === 'function' ? promise() : promise
  const hasNotify = notify => Object.keys(notify).length

  try {
    const data = await (Array.isArray(promise) ? Promise.all(promise) : primiseToBeExec)
    hasNotify(notifySuccess) && Notify.create({ type: 'success', ...notifySuccess })
    return { data, error: null }
  } catch (error) {
    hasNotify(notifyError) && Notify.create({ type: 'error', ...notifyError })
    return { data: null, error }
  } finally {
    onLoading && onLoading(false)
    Loading.hide()
  }
}
Enter fullscreen mode Exit fullscreen mode

I added a callback onLoading so I can do somenthing while function is running.

const { data, error } = await promiseHandler(callbackFn, { onLoading: value => console.log('is loading?', value) })

Collapse
 
bibekkakati profile image
Bibek

👨🏼‍💻

Collapse
 
calinzbaenen profile image
Calin Baenen

Better yet, just use GoLang.

Collapse
 
bibekkakati profile image
Bibek

Good idea. 😉

Collapse
 
calinzbaenen profile image
Calin Baenen • Edited

Of course it is! Everyone knows Go is the best language objectively.

Collapse
 
calinzbaenen profile image
Calin Baenen

I've reconsidered. . .

Collapse
 
calinzbaenen profile image
Calin Baenen

Thinking back, I spoke very unwise words.
It should be "Better yet, just use Rust." — Rust's error handling is exceptional. :þ

Collapse
 
jsnanigans profile image
Brendan Mullins

Nice, but I would personally use an object instead of the array as a return type

Collapse
 
bibekkakati profile image
Bibek

I prefer array because if we use object, destructuring will be an issue if there is more than one block in same level. Also we need to be aware of the property name.

Collapse
 
jankapunkt profile image
Jan Küster

Property name can be set by convention across your whole project. Let's say { error, result } which is to me most verbose and would not lead to accidental access compared to array indices

Thread Thread
 
bibekkakati profile image
Bibek

Yeah, that is right. But in case of multiple blocks in the same scope it can create issue, as you will not be able de-structure it like this

const {error, result} = await method();

// second method call will create an issue
const {error, result} = await method2();
Enter fullscreen mode Exit fullscreen mode

But in case of array, you can use other variable name in the second method call.

Feel free to correct me, if I am missing something.

Thread Thread
 
jsnanigans profile image
Brendan Mullins

a few ways you could avoid this:

const responseOne = await methodOne();
if (responseOne.error) return

const responseTwo = await methodTwo();
if (responseTwo.error) return

// of if you want destructuring
const {error: methodOneError, result: resultOne} = await methodOne();
if (methodOneError) return
const {error: methodTwoError, result: restultTwo} = await methodTwo();
if (methodTwoError) return
Enter fullscreen mode Exit fullscreen mode

Im not saying the array descructuring is worse, its just a prefference

Thread Thread
 
erikhofer profile image
Erik Hofer

You can rename the variable when destructuring an object.

const {error, result} = await method();

const {error: error2, result: result2} = await method2();
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
bibekkakati profile image
Bibek

@jsnanigans Yeah, It's just a preference.

Thread Thread
 
bibekkakati profile image
Bibek • Edited

@erikhofer I am aware of that.

Collapse
 
grzegorzbielski profile image
Grzegorz Bielski

I'm not a huge fan of this pattern.
The idea of explicitly returning errors rather than throwing unchecked errors is great, but the proposed implementation is flawed - we need to always check the elements of the tuple before using the value to see if there is an error which will become error-prone rather quickly.
Fortunately, this is an already solved problem, known in other langues as Either or Result type, in which all of these ifs are abstracted away in form of map and flatMap / chain operations.
Potential implementation: gigobyte.github.io/purify/adts/Eit...

Collapse
 
bibekkakati profile image
Bibek

Got your point. After all it's all about own preference.

Collapse
 
chinmaykb profile image
Chinmay Kabi

I saw this in a Fireship video, ditto 👀

Collapse
 
bibekkakati profile image
Bibek

Is it? Need to check it.

I saw this in a 3 years old article.

Collapse
 
njavilas2015 profile image
njavilas2015 • Edited

in typescript

async function goCatch<T>(promise: Promise<T>): Promise<[T | null, null | Error]> {
    try {

        const result: Awaited<T> = await promise

        return [result, null]

    } catch (err) { return [null, err] }
}


const main = async (params: params) => {


    const [store, err] = await goCatch(qbpbspfzfeyudz.create(params.store))

}

Enter fullscreen mode Exit fullscreen mode
Collapse
 
outthislife profile image
Talasan Nicholson

Can just do if (error) though.

Collapse
 
bibekkakati profile image
Bibek

Yeah, we can do that. Sometimes I just like to check explicitly.

Collapse
 
clsource profile image
Camilo

Nice idea. Thanks for sharing :)

Collapse
 
bibekkakati profile image
Bibek

I'm glad that you liked it. 😊

Collapse
 
bibekkakati profile image
Bibek

Got it. Thank you for sharing that.