AS3: Tratamento de Erros

Existem 2 tipos de erros no Flash: Erro de compilação e erro em tempo de execução.
Provavelmente você já se deparou diversas vezes com ambos, mas por via das dúvidas vou refrescar a sua memória.

Erros de compilação

Erros de compilação ocorrem no momento em que o programa está em processo de compilação, e o software se depara com alguma instrução incorreta. Erros de sintaxe são os mais comuns. Por exemplo, as seguinte instrução:

1
this._x = 10;

Irá resultar no erro:

Warning: 1058: Migration issue: The property _x is no longer supported.
Use the DisplayObject.x property instead.

Um erro bem comum para quem está saindo do AS2 e começando no AS3. Uma vez que o erro ocorre, o programa abandona o processo de compilação. O erro deve ser corrigido e o programa compilado novamente.

Porém nem todos os erros pode ser percebidos no momento da compilação, e é por isso que existem os erros em tempo de execução.

Erros em tempo de execução

Como o nome já diz, são erros que ocorrem durante a execução do programa, e são tão comuns quanto os erros de compilação. Vejamos o seguinte exemplo:

1
2
3
4
this.getChildAt(0);
           
this._bola = new Bola();
this.addChild(this._bola);

O resultado:

RangeError: Error #2006: The supplied index is out of bounds.
at flash.display::DisplayObjectContainer/getChildAt()
at Animation()

Sim, é um exemplo estúpido, mas acredite, muita gente já viu esse erro, e o motivo foi o mesmo, tentamos acessar um índice que ainda não existia.

Erros em tempo de execução sempre ocorrem, sempre irão ocorrer, e em alguns casos é inevitável que eles ocorram, porque existe situações onde não se pode prever quando e como um erro irá acontecer.

Para você entender o que eu quis dizer com “inevitavel”, vamos imaginar que você esteja criando uma aplicação que recebe dados XML de uma página. O formato do XML seria este:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<users>
    <user>
        <name>Mozart Petter</name>
        <email>mozart@dominio.com</email>
   
</users>

Carregamos este XML da seguinte forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * @private
 * Handler do evento COMPLETE, disparado quando os dados foram completamente carregados.
 *
 * @param evt Objeto Event.
 */

private function handleDataComplete(evt:Event):void
{
    var xmlData:XML = new XML(evt.target.data);
}

/**
 * @private
 * Carrega os dados externos.
 */

private function loadData():void
{
    var request:URLRequest = new URLRequest("data.php");
    var loader:URLLoader = new URLLoader();
   
    loader.addEventListener(Event.COMPLETE, this.handleDataComplete);
   
    loader.load(request);
}

Bem simples, carregamos os dados em data.php e os convertemos para XML no handler.
E ao compilar nós temos…

TypeError: Error #1085: The element type "user" must be terminated by the matching end-tag "".
at User/handleDataComplete()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()

Eita, peraí?! Como assim? Ah! O XML tá errado… mas como poderíamos saber, se ele é gerado através do PHP?

Enfim, erros ocorrem, principalmente quando interagimos com dados externos, onde não temos como prever quedas de banco de dados, erros de formatação dos dados, etc.

Então, o que fazemos num caso como esse? O usuário fica olhando que nem um “banana” pra tela enquanto a aplicação não faz absolutamente nada a respeito?

Try, Catch and Finally

Esse trio vai nos ajudar no que se diz respeito a tratar os erros. O primeiro carinha ali, se chama try. O try é o cara que irá tentar executar uma instrução que tende a gerar um erro.

Catch é o cara que irá “interceptar” os erros, se ocorrerem. Desta forma, conseguimos decidir o que iremos fazer com o erro capturado, antes que ele abra a boca.

E o finally é o cara onde podemos executar uma instrução, com a segurança de que ela será feita, independente do que acontecer com o try ou com o catch.

Vamos ao exemplo. Nosso XML tem um erro de sintaxe, então, de alguma forma devemos avisar o usuário. O primeiro passo, é colocarmos a instrução que pode resultar em erro, dentro do try.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * @private
 * Handler do evento COMPLETE, disparado quando os dados foram completamente carregados.
 *
 * @param evt Objeto Event.
 */

private function handleDataComplete(evt:Event):void
{
    try
    {
        var xmlData:XML = new XML(evt.target.data);
    }
    catch (err:Error)
    {
        trace("Um erro ocorreu senhor, mas foi capturado.");
    }
}

Colocamos a instrução dentro do try, e logo abaixo o catch. Entenda que o try nunca vai em uma “missão” sozinho, ele sempre precisa estar acompanhado do catch ou do finally.

Se você compilar o FLA agora, verá que o trace dentro do catch irá ser executado. Isso porque o catch interceptou o erro, e o manteve ali, escondido. A partir deste ponto, podemos tomar uma decisão quanto ao que fazer com o erro capturado. Podemos eliminá-lo… ou libertá-lo com algumas condições.

1
2
3
4
5
6
7
8
try
{
    var xmlData:XML = new XML(evt.target.data);
}
catch (err:Error)
{
    trace("O seguinte erro ocorreu: " + err.message);
}

O seguinte erro ocorreu: Error #1085:
The element type "user" must be terminated by the matching end-tag "".

Ou seja, dentro do catch nós podemos fazer qualquer coisa com o erro, podemos exibi-lo em um feedback para o usuário, gravá-lo em um log ou interromper a execução do código, para que a coisa não se torne uma bola de neve.

Em alguns casos, independente do que ocorrer no try e no catch, você pode querer executar algo. Mesmo em situações onde a execução do script seja cancelada. E é aí que entra o finally.

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
    var xmlData:XML = new XML(evt.target.data);
}
catch (err:Error)
{
    trace("O seguinte erro ocorreu: " + err.name);
    return;
}
finally
{
    trace("Missão concluída");
}

O seguinte erro ocorreu: TypeError
Missão concluída

Perceba que o bloco do finally foi executado, mesmo existindo um return dentro do bloco do catch. O que não ocorreria se ele não existesse:

1
2
3
4
5
6
7
8
9
10
11
try
{
    var xmlData:XML = XML(evt.target.data);
}
catch (err:Error)
{
    trace("O seguinte erro ocorreu: " + err.name);
    return;
}

trace("Missão concluída");

O seguinte erro ocorreu: TypeError

Então, se existir algo que você necessite executar, independente do que ocorrer, essa instrução deve ser feita no finally.

Um detalhe que vale ser lembrado é que nós podemos tratar mais de um tipo de erro ao mesmo tempo. O pacote errors do Flash tem alguns tipos, e também podemos achar outros no Top Level. Vale a pena dar uma olhada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try
{
    this.getChildAt(12);
   
    var xmlData:XML = new XML(evt.target.data);
}
catch (err:RangeError)
{
    trace("Um RangeError ocorreu: \n" + err.message);
}
catch (err:TypeError)
{
    trace("Um TypeError ocorreu: \n" + err.message);
}

Se um erro do tipo RangeError ou TypeError ocorrer, ele será tratado. No caso do exemplo acima, o RangeError ocorre primeiro.

Um RangeError ocorreu:
Error #2006: The supplied index is out of bounds.

Se o RangeError não ocorrer, o TypeError será tratado. Desta forma temos como especificar tratamentos diferenciados para cada tipo de erro.
Mas… se um tipo diferente de erro ocorrer, ele não será capturado, pois o catch foi designado apenas para perseguir esses dois tipos de erro.

Portanto, só especifique um tipo de erro se tiver certeza que ele pode ocorrer. O type Error é um tipo genérico, uma vez que todos os outros tipos herdam ele. Se você especificar o tipo Error, todos os erros serão pegos e tratados da mesma maneira.

Criando os seus próprios erros

Você pode também disparar os seus próprios erros se achar necessário. O comando throw serve exatamente para isso.

Assim como as classes do Flash, as suas classes muitas vezes tratam de dados ou comportamentos específicos, e se a coisa não funcionar conforme o previsto, você pode disparar um erro, para que o mesmo seja tratado.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Envia uma nova mensagem.
 *
 * @param message Texto da mensagem a ser enviada.
 */

public function sendMessage(message:String):void
{
    if (!message)
    {
        throw new Error("Nenhum texto foi especificado para a mensagem.");
    }
   
    trace("Mensagem enviada: " + message);
}

O seguinte método tem como finalidade enviar uma mensagem, mas se nenhuma mensagem for especificada, ele irá gerar um erro. Vamos executar tal método e ver o resultado.

1
this.sendMessage(null);

Error: Nenhum texto foi especificado para a mensagem.
at Message/sendMessage()
at Message()

Uma vez que a mensagem é nula, o erro aparece. Desta forma temos como disparar erros em determinados pontos, para que eles sejam tratados, e assim, evitar que o usuário fique “preso” naquele erro.

1
2
3
4
5
6
7
8
try
{
    this.sendMessage(null);
}
catch (err:Error)
{
    trace("O seguinte erro ocorreu: " + err.message);
}
O seguinte erro ocorreu: Nenhum texto foi especificado para a mensagem.

Resumindo, o tratamento de erros nos possibilita decidir de que forma o erro será resolvido, evitando situações onde a aplicação tenha um comportamento inesperado.

Se tiverem dúvidas, o espaço para comentários está aí.

2 Comments

  1. Ótimo post!
    Não sabia nada sobre tratamento de erros, mas agora vou emplementar tudo isso nos meus arquivos.

  2. Tratamento de erros sempre é bom, uma outra maneira além de pegar o message do erro também é legal pegar o tipo do erro com a propriedade name mas pode ser usado a função getStackTrace() que exibe uma mensagem informando o nome, a mensagem e também as linhas do código em que o erro aconteceu, é interessante por faciliar o rastreamento do erro.

Leave a Comment