DEV Community

loading...
Cover image for Error Handling in JavaScript  (Golang Style)

Error Handling in JavaScript (Golang Style)

Bibek
Hey, ๐Ÿ‘‹ I'm a full-stack developer with expertise in the MERN stack. I do freelancing, contract jobs for my living ๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป
Originally published at blog.bibekkakati.me ใƒป2 min read

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.



Discussion (30)

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
DarkWiiPlayer

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
DarkWiiPlayer • 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 Author

Yeah. That can be way of implementing it.

Collapse
bibekkakati profile image
Bibek Author • Edited

Yeah. Utility function can be very helpful.

Collapse
lukeshiru profile image
LUKE็Ÿฅใ‚‹

Interesting approach! Remember that you don't have to use async/await if you don't need it, so the wrapper could look something like this:

/**
 * @template Value
 * @param {Promise<Value>} promise
 */
const promiseCouple = promise =>
    promise.then(
        /** @returns {[Value?, unknown?]} */
        value => [value],
        /** @returns {[Value?, unknown?]} */
        error => [, error]
    );
Enter fullscreen mode Exit fullscreen mode

And then you can use it just like this:

promiseCouple(methodCall()).then(([result, error]) => {
    if (error === undefined) {
        // Do your stuff here
    }
});
Enter fullscreen mode Exit fullscreen mode

Or like this:

const [result, error] = await promiseCouple(methodCall());

if (error === undefined) {
    // Do your stuff here
}
Enter fullscreen mode Exit fullscreen mode

You might have noticed that I used undefined instead of null. This is basically because in JS at least, is not the best practice to use null (it evaluates to type "object", is not the actual default for "emptiness", but actually undefined is, and so on).

Cheers!

Collapse
bibekkakati profile image
Bibek Author

Got it. Thank you for sharing that.

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 Author

๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป

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 Author

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 Author

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 Author

@jsnanigans Yeah, It's just a preference.

Thread Thread
bibekkakati profile image
Bibek Author • Edited

@erikhofer I am aware of that.

Collapse
baenencalin profile image
Calin Baenen

Better yet, just use GoLang.

Collapse
bibekkakati profile image
Bibek Author

Good idea. ๐Ÿ˜‰

Collapse
baenencalin profile image
Calin Baenen • Edited

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

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 Author

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

Collapse
outthislife profile image
Talasan Nicholson

Can just do if (error) though.

Collapse
bibekkakati profile image
Bibek Author

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 Author

I'm glad that you liked it. ๐Ÿ˜Š

Collapse
cermitio profile image
Cermitio

Thanks a lot, I will try to use it on practice

Collapse
bibekkakati profile image
Bibek Author

Sure :)

Collapse
chinmaykb profile image
Chinmay Kabi

I saw this in a Fireship video, ditto ๐Ÿ‘€

Collapse
bibekkakati profile image
Bibek Author

Is it? Need to check it.

I saw this in a 3 years old article.