Lançando Erros com o Operador throw em JavaScript
O interpretador de JavaScript lança erros para diversas situações, como ao chamar uma função inexistente ou ao tentar reatribuir um valor a uma constante. No entanto, também podemos lançar nossos próprios erros e definir condições sob as quais os erros serão lançados.
Por exemplo, considere a seguinte situação:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
const tom = new Person("Tom", -123);
tom.print(); // Name: Tom Age: -123
A classe Person
descreve uma pessoa. O construtor da classe recebe valores para as propriedades name
(nome) e age
(idade). É de senso comum que a idade não pode ser negativa. No entanto, de acordo com a lógica da classe, nada impede que um objeto Person
seja criado com um valor negativo para idade. Do ponto de vista do interpretador JavaScript não há erro, mas segundo a lógica e o bom senso, isso é um erro. Como podemos corrigir essa situação? Existem várias maneiras, e uma delas é lançando uma exceção.
Para lançar uma exceção, usamos o operador throw
, após o qual especificamos informações sobre o erro:
throw informacaoSobreErro;
As informações sobre o erro podem ser representadas por qualquer objeto.
Então, vamos lançar uma exceção quando um valor negativo for passado para a propriedade age
no construtor de Person
:
class Person {
constructor(name, age) {
if (age < 0) throw "A idade deve ser positiva";
this.name = name;
this.age = age;
}
print() {
console.log(`Name: ${this.name} Age: ${this.age}`);
}
}
try {
const tom = new Person("Tom", -123); // Uncaught A idade deve ser positiva
tom.print();
} catch (error) {
console.log("Ocorreu um erro");
console.log(error); // A idade deve ser positiva
}
Consequentemente, ao chamar o construtor de Person
, será lançada uma exceção e o programa terminará com erro. A informação sobre o erro, que foi especificada após o operador throw
, aparecerá na console do navegador:
Uncaught A idade deve ser positiva
Como em casos gerais, podemos lidar com esse erro usando um bloco try
...catch
.
Uso do throw em try..catch..finally
O operador throw
pode ser chamado em diferentes contextos, como dentro de um bloco try
. Isso pode ser útil por várias razões. Em primeiro lugar, podemos lidar imediatamente com o erro. Em segundo lugar, podemos usar o bloco finally
para definir algumas ações finais que serão executadas, mesmo que um erro seja lançado. Por exemplo:
// classe de um banco de dados fictício
class Database {
constructor() {
this.data = ["Tom", "Sam", "Bob"];
}
// obter dados
getItem(index) {
this.open();
if (index > 0 && index < this.data.length) return this.data[index];
else throw "Índice incorreto";
this.close(); // essa linha não será executada se uma exceção for lançada
}
// abrir o banco de dados
open() {
console.log("Conexão com o banco de dados estabelecida");
}
// fechar o banco de dados
close() {
console.log("Conexão com o banco de dados fechada");
}
}
const db = new Database();
try {
db.getItem(5); // retorna o elemento obtido
} catch (err) {
console.error(err); // se ocorrer um erro, tratamos aqui
}
Neste trecho, a classe Database
é uma classe de um banco de dados fictício. Todos os dados são armazenados em um array data
. Para interagir com o banco de dados, três métodos são definidos. Os métodos open
e close
abrem e fecham a conexão com o banco de dados, respectivamente. O método getItem recupera um elemento do array data por índice. Se o índice for incorreto, um erro é lançado. Antes de recuperar o elemento por índice, o método getItem deve abrir a conexão com o método open
, e após recuperar o elemento, fechar com o método close
. No entanto, no exemplo acima, se um erro for lançado, o fechamento da conexão não ocorrerá:
else
throw "Índice incorreto";
this.close(); // essa linha não será executada se uma exceção for lançada
Consequentemente, se um índice incorreto for passado ao método getItem
, o output da console será:
Conexão com o banco de dados estabelecida Índice incorreto
Mas o que fazer se ainda precisarmos chamar o método close
? Podemos colocar sua chamada no bloco finally
:
class Database {
constructor() {
this.data = ["Tom", "Sam", "Bob"];
}
// obter dados
getItem(index) {
this.open();
try {
if (index > 0 && index < this.data.length) return this.data[index];
else throw "Índice incorreto";
} finally {
// mesmo que um erro seja lançado, este bloco é executado
this.close(); // essa linha será executada mesmo se uma exceção for lançada
}
}
// abrir o banco de dados
open() {
console.log("Conexão com o banco de dados estabelecida");
}
// fechar o banco de dados
close() {
console.log("Conexão com o banco de dados fechada");
}
}
const db = new Database();
try {
db.getItem(5); // retorna o elemento obtido
} catch (err) {
console.error(err); // se ocorrer um erro, tratamos aqui
}
E agora o output da console será diferente:
Conexão com o banco de dados estabelecida Conexão com o banco de dados fechada Índice incorreto