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í.
Ótimo post!
Não sabia nada sobre tratamento de erros, mas agora vou emplementar tudo isso nos meus arquivos.
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.