Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Try-catch oneliner

If you search npm with query try await you will find a big modules list (you can find it in the boottom).

What all this developers want is just more clear way to use try catch. All of them suggest to use:

const tryCatch = require('try-catch');
const [error, data] = tryCatch(JSON.parse, 'hello');

Instead of

let error, data;

try {
  data = JSON.parse('hello');
} catch (e) {
  error = e;
}

Because this is much more clean way to handle things. We get less code in a more readable form. What I want to suggest is new operator try (it can use different name). And used this way:

const [error, data] = try JSON.parse('hello');

When we have a promise, we can use try this way:

const {readFile} = require('fs').promises;
const [error, data] = try await readFile('hello');

This is also a pattern used in a Go language.

This construction can be used as a library. But it so clean and powerful, why not just add it the language to a void producing more and more the same libraries.

FAQ

Why array destructuring used?

Because when you use object destructuring you chose names of variables, for example:

const {error, data} = try await readFile('hello');
const {error: error2, data: data2} = try await readFile('hello2');

This can very fast became a mess.

Why first value is error?

Because you will not always have second argument, and because you always should check if error happend. This is similar to node.js callback style:

fs.readFile('hello.txt', (error, data) => {
  // you should check if error happend first
});

So this is more or less obvious for most developers.

Isn't this is a problem that only one function is a try block?

No. Because when you use async-await in most places you can avoid try catch blocks, but you should use it in the main function to handle errors:

async main() {
  await start();
  await doSomething();
  await end();
}

// will be possible with top-level-await
try {
  await main();
} catch(e) {
  console.log(e);
}

// or just
main().catch(console.log)

When error occures in one of function calls: start, doSomething, end we will catch it in main().catch. Also such things gives ability to developer use try when necessary to change the way things going, for example:

async main() {
  const [startError] = try await start();
  
  if (startError.message === 'critiq')
    throw startError;
  
  const [someError] = await doSomething();
  if (someError.message !== 'done already')
    throw someError;
  
  await end();
}

Why no just keep using big old try-catch block?

Because this produces more code thet can be used, make this harder to read, consider this example:

async main() {
  try {
    await start();
    await doSomething();
    await end();
  } catch (e) {
  }
}

You have more indentation, more code constructs, and everything looks like a mess, and a lot module authors thinks like that.

Why new operator?

Because try can be used in a similar way as typeof, await, etc. It's and maximizes the potential of destructuring assignment making the code more readable obvious, clean and simple.

Why try before await?

Because you should fullfil the promise first and then handle it's result. Similar to:

typeof await promise();

References

Modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.