What is "Error cause" in Javascript?

Asked

Viewed 65 times

3

It is common to have mistakes thrown and these errors have to be captured and treated in different ways, according to the context. I see (and use) the practice of creating custom classes of errors (which I will still ask a specific question on the subject), such as (self-explanatory):

export class InternalServerException extends Error{
  ...
}

export class APIResponseException extends Error{
  ...
}

export class DataBaseConnectionException extends Error{
  ...
}

Error handling done always inside the block catch, but with the use of ifs to perform a particular operation according to the error instance:

try {
  ...
  executaAlgumaCoisa()
  ...
} catch (error) {
  if (error instanceof APIResponseException) {
    // trata para o erro de API
  }

  if (error instanceof InternalServerException) {
    // trata para o erro interno
  }
}

What sucks about all this is doing ifs and still have to import these classes only for instance checking and know where the error comes from, whether it is from the API or internal application error, where these classes are the causes(cause) errors, in order to be able to perform actions according to.

Any example, executable to demonstrate:

class NumeroParError extends Error {
  constructor(message) {
    super(message)
    this.name = 'Error de Par'
  }
}

class NumeroImparError extends Error {
  constructor(message) {
    super(message)
    this.name = 'Error de Ímpar'
  }
}

try {
  const numero = Math.floor(Math.random() * 10)
  if (numero % 2) {
    throw new NumeroImparError(numero)
  } else {
    throw new NumeroParError(numero)
  }
} catch (err) {
  if (err instanceof NumeroParError) {
    console.log(`${err.name}. Motivo: ${err.message}`)
  } 
  if (err instanceof NumeroImparError) {
    console.log(`${err.name}. Motivo: ${err.message}`)
  } 
}

The new Javascript feature called Error causes has come to give greater semantics to bug objects and minimize the amount of code to be written.

  • How this feature works?
  • It will be useful to the point of not having to create custom error classes like InternalServerException and APIResponseException?

1 answer

2


The proposal Error Cause is basically an extension to native language error constructs to allow the addition of contextual information to the error to be instantiated. This information is a mere reference to the error that caused another error.

The causes of error are no very deep modification in the language. They are merely extensions to errors which, by adding more information about the cause of an error, help in your diagnosis.

Without the cause of errors properly attached to any of these errors, there is, in fact, no information in the instance error that tells exactly what was its cause. Of course we can diagnose by stack trace, but the information would not be in the application objects, but rather in a string that is not very convenient to manipulate.

To solve this, some people could opt for alternatives such as:

  • Create a subclass of Error which allows the addition of a property indicating where the error in question came from;
  • Extend the error message by adding information not only about the immediate error, but also about the error that caused the immediate error. It doesn’t scale very well.
  • Libraries for error management.

However, note that none of this is standardized. Each programmer could opt for a different alternative on how to deal with the need to add contextual information about the cause of the error.

To solve this, they decided to standardize this at the language level. It was probably inspired by something like Error::source of Rust and the like which must exist in other resources.

Thus, an error can be launched already attaching the error that caused it in its own construction. Stealing the example of the proposal itself:

async function doJob() {
  const rawResource = await fetch('//domain/resource-a')
    .catch(err => {
      throw new Error('Download raw resource failed', { cause: err });
    });
  const jobResult = doComputationalHeavyJob(rawResource);
  await fetch('//domain/upload', { method: 'POST', body: jobResult })
    .catch(err => {
      throw new Error('Upload job result failed', { cause: err });
    });
}

try {
  await doJob();
} catch (e) {
  console.log(e);
  console.log('Caused by', e.cause);
}
// Error: Upload job result failed
// Caused by TypeError: Failed to fetch

Note that by adding information about what actually caused the error, the error message itself can be greatly simplified. Compare to messages like:

  • Could not download Resource because failed to fetch.
  • Could not upload Resource because failed to fetch.

In this case, some might argue that the cause is doing little - and I would agree. However, in situations where errors have long chains of interdependence in relation to the cause, it can be of great help to link them by source, especially since cause can actually create a type of "linked list of errors". See:

const errorA = new Error('first raised error');
const errorB = new Error('second raised error', { cause: errorA });
const errorC = new Error('third raised error', { cause: errorB });
const errorD = new Error('fourth raised error', { cause: errorC });

try {
  throw errorD;
} catch (err) {
  console.log(err.message);
  while (err = err.cause) {
    console.log(`  - Caused by: ${err.message}`);
  }
}

I tested in Firefox, which already supports the new addition (not yet working in Chrome), and the output was like this:

Saída do código acima

Note that, as the errors are linked together (in a very similar way to a linked list), it is very simple to get information about the cause in the application itself. You can use the stack trace also, but as it is not well standardized, the task can be quite complicated.


Regarding the sub-classes of error posed by the AP in the question, the new proposal does not help well with this.

Of course, with the new property, the need to create subclasses for contextualize error may decrease, but if you want to treat each error variation individually within a block catch, still have to do those various checks with the if. The new proposal is not intended to help with the discrimination of errors, but to extend them with causal information.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.