Processamento de erros

Nota

O comportamento descrito neste artigo só está disponível quando a funcionalidade de pré-visualização Gestão de erros ao nível da fórmula através das Definições>Caraterísticas futuras>Pré-visualização está ativada. Mais informações: Controlar quais as funcionalidades ativadas

Erros acontecem. As redes caiem, o armazenamento fica cheio, valores inesperados entram. É importante que a sua lógica continue a funcionar corretamente perante problemas potenciais.

Por predefinição, os erros fluem através das fórmulas de uma aplicação e são reportados ao utilizador final da aplicação. Deste modo, o utilizador final sabe que algo inesperado ocorreu, pode potencialmente corrigir o problema com uma entrada diferente ou pode reportar o problema ao proprietário da aplicação.

Enquanto criador de aplicações, pode controlar os erros na aplicação:

  • Detetar e processar um erro. Se for possível que ocorra um erro, as fórmulas da aplicação podem ser escritas para detetar a condição de erro e repetir a operação. O utilizador final não precisa de ficar preocupado com a ocorrência de um erro porque o criar considerou essa possibilidade. Isto é feito com as funções IfError, IsError e IsErrorOrBlank dentro de uma fórmula.
  • Reportar um erro. Se um erro não for processado na fórmula onde foi encontrado, o erro é depois adicionado ao processador App.OnError. Aqui, o erro já não pode ser substituído, uma vez que já ocorreu e faz parte dos cálculos de fórmulas. No entanto, pode utilizar App.OnError para controlar como o erro é reportado ao utilizador final, incluindo a supressão do reporte de erros em conjunto. App.OnError também fornece um ponto de estrangulamento comum para o relatório de erros em todo o aplicativo.
  • Criar e voltar a lançar um erro. Finalmente, poderá detetar uma condição de erro com a sua própria lógica, uma condição específica da sua aplicação. Utilize a função Error para criar erros personalizados. A função Error também é utilizada para voltar a lançar um erro após ter sido interrogado em IfError ou App.OnError.

Começar

Comecemos com um exemplo simples.

  1. Crie um novo ecrã numa Aplicação de tela do Power Apps.
  2. Insira um controlo TextInput. Assumirá a predefinição do nome TextInput1.
  3. Insira um controlo Label.
  4. Defina a propriedade Text do controlo Label para a fórmula
1/Value( TextInput1.Text )

Faixa de erro apresentada com

Temos um erro devido ao texto predefinido de um controlo TextInput ser "Text input", que não pode ser convertido para um número. Por predefinição, isto é uma coisa boa: o utilizador final receberá uma notificação de que algo não está a funcionar conforme esperado na aplicação.

Obviamente, não queremos que o utilizador receba um erro sempre que este iniciar a aplicação. Provavelmente, "Text input" não é a predefinição certa para a caixa de entrada de texto. Para remediar isto, alteremos a propriedade Default do controlo TextInput para:

Blank()

Faixa de erro apresentada com

Hmm, agora temos um erro diferente. As operações matemáticas com em branco, tais como a divisão, irão passar o valor em branco para um zero. E isto está agora a causar um erro de divisão por zero. Para remediar isto, temos de decidir qual o comportamento apropriado para esta situação nesta aplicação. A resposta poderá ser mostrar em branco quando a entrada de texto estiver em branco. Conseguimos isto ao encapsular a nossa fórmula com a função IfError:

IfError( 1/Value( TextInput1.Text ), Blank() )

Não foi apresentada nenhuma faixa de erro, um erro devido a um valor em branco foi substituído por um espaço em branco

Agora o erro é substituído por um valor válido e a faixa de erro desapareceu. No entanto, podemos ter exagerado, o IfError que utilizámos abrange todos os erros, incluindo a escrita de valores incorretos, tal como "hello". Podemos resolver este assunto ajustando o nosso IfError para processar apenas o caso de divisão por zero e voltando a lançar todos os outros erros:

IfError( 1/Value( TextInput1.Text ), 
         If( FirstError.Kind = ErrorKind.Div0, Blank(), Error( FirstError ) ) )

Não apresentado faixa de erro, um erro devido especificamente à divisão por zero foi substituído por um espaço em branco; caso contrário, o erro é gerado novamente

Por isso, vamos então executar a nossa aplicação e experimentar alguns valores diferentes.

Sem qualquer valor, como quando a aplicação é iniciada, não existe resposta apresentada, uma vez que o valor predefinido está em branco, mas também não existe nenhum erro mostrado, uma vez que IfError substitui o erro de divisão por zero.

Sem resposta apresentado e sem faixa de erro

Se escrevermos um 4, obteremos o resultado esperado de 0,25:

0,25 apresentado e sem faixa de erro

Se escrevermos algo incorreto, como hello, receberemos uma faixa de erro:

nenhum valor apresentado e faixa de erro apresentada para a incapacidade de converter

Trata-se de um exemplo simples de introdução. O processamento de erros pode ser feito de muitas maneiras diferentes, dependendo das necessidades da aplicação:

  1. Em vez de uma faixa de erro, poderíamos ter mostrado "#Error" no controlo label com a fórmula. Para manter os tipos de substituição compatíveis com o primeiro argumento para IfError é necessário converter explicitamente o resultado numérico numa cadeia de texto com a função Text.
    IfError( Text( 1/Value( TextInput1.Text ) ), 
             If( FirstError.Kind = ErrorKind.Div0, Blank(), "#Error" )
    
    sem faixa de erro e, em vez disso, #Error é mostrado como resultado
  2. Em vez de encapsular esta instância específica com IfError, poderíamos ter escrito um processador App.OnError centralizado. Não podemos substituir a cadeia mostrada com "#Error", uma vez que o erro já aconteceu e App.OnError só é fornecido para controlar os relatórios.
    If( FirstError.Kind <> ErrorKind.Div0, Error( FirstError ) )
    

Propagação de erros

Os erros fluem tanto através de fórmulas, tal como no Excel. Por exemplo, no Excel, se a célula A1 tiver a fórmula =1/0, A1 apresentará o valor de erro #DIV0!:

Folha de cálculo do Excel com A1=1/0 e #DIV/0! mostrados na célula

Se a célula A2 se referir a A1 com uma fórmula como =A1*2, o erro propaga-se através dessa fórmula também:

Folha de cálculo do Excel com A2=A1*2 e #DIV/0! mostrados na célula

O erro substitui o valor que, de outro modo, teria sido calculado. Não há resultado para a multiplicação na célula A2, apenas o erro da divisão em A1.

O Power Fx funciona da mesma forma. Regra geral, se um erro for fornecido como um argumento para uma função ou operador, a operação não ocorrerá e o erro de entrada fluirá como o resultado da operação. Por exemplo, Mid( Text( 1/0 ), 1, 1 ) irá devolver um erro de Divisão por Zero, à medida que o erro mais interno passa através da função Text e da função Mid:

Faixa de erro a mostrar uma operação inválida: divisão por zero

Em geral, os erros não fluem através das propriedades de controlo do Power Apps. Vamos expandir o exemplo anterior com um controlo adicional que é apresentado se a propriedade Text da primeira etiqueta estiver num estado de erro:

Não é apresentado nenhum erro no controlo second label

Não há problema se os erros não se propagarem através de um controlo porque o sistema observará erros na entrada em todas as propriedades de controlo. O erro não se perde.

A maior parte das funções e operadores seguem a regra "erro entra, erro sai", mas existem algumas exceções. As funções IsError, IsErrorOrBlank e IfError foram concebidas para trabalharem com erros, pelo que podem não obter o erro mesmo se um passar por eles.

Observar erros

Os erros só são observados depois de o valor ser utilizado.

Como resultado, as funções If e Select também poderão não obter um erro se um for passado. Considere a fórmula If( false, 1/0, 3 ). Existe um erro de divisão por zero presente nesta fórmula, mas, visto que If não está a tomar esse ramo devido ao false, o Power Fx e o Power Apps não irão reportar um erro:

Não é apresentada nenhuma faixa de erro com a função If na propriedade Text da etiqueta

A utilização da função Set com um erro não reportará um erro no momento em que o erro é colocado na variável. Por exemplo, no Power Apps, eis uma fórmula em App.OnStart que coloca um erro de divisão por zero na variável x:

Não é apresentada nenhuma faixa de erro com a chamada da função Set em App.OnStart

Não é reportado nenhum erro porque x não está a ser referenciado. No entanto, no momento em que adicionamos um controlo label e definimos a respetiva propriedade Text como x, é apresentado o erro:

Faixa de erro mostrada com o controlo label que referencia a variável x

Pode observar erros numa fórmula com as funções IfError, IsError e IsErrorOrBlank. Com estas funções, pode obter um valor alternativo, tomar uma ação alternativa ou modificar o erro antes de ser observado e reportado.

Erros de reporte

Depois de um erro ser observado, o passo seguinte será comunicar o erro ao utilizador final.

Ao contrário do Excel, nem sempre existe um local prático para mostrar um resultado de erros, uma vez que o resultado de uma fórmula pode guiar uma propriedade como as coordenadas X e Y de um controlo para o qual não existe nenhum local conveniente para mostrar algum texto. Cada anfitrião do Power Fx controla a forma como os erros são apresentados por fim ao utilizador final e o controlo que o criador tem sobre este processo. No Power Apps, é apresentada uma faixa de erro e App.OnError é utilizado para controlar a forma como o erro é reportado.

É importante que note que o App.OnError não pode substituir o erro, da mesma forma que IfError. No momento em que App.OnError for executado, o erro já ocorreu e o resultado propagou-se através de outras fórmulas. App.OnError controla apenas como o erro é relatado ao utente final e fornece um gancho para o fabricante registrar o erro, se desejado.

As variáveis de âmbito FirstError e AllErrors fornecem informações de contexto acerca do erro ou erros. Fornece informações sobre o tipo de erro e a origem do erro, bem como o local onde foi observado.

Parar após um erro

As fórmulas de comportamento suportam a tomada de ação, a modificação de bases de dados e alteração do estado. Estas fórmulas permitem que mais do que uma a ação seja efetuada numa sequência utilizando o operador de encadeamento ; (ou ;;, dependendo da região).

Neste caso, por exemplo, o controlo grid está a mostrar o que está na tabela T. Cada botão selecionar alterações ao estado nesta tabela com duas chamadas Patch:

Animação a mostrar os dois registos na tabela T a ser atualizados com números aleatórios depois de cada clique do botão

Numa fórmula de comportamento encadeada, as ações não param após o primeiro erro. Modifiquemos o nosso exemplo para transmitir um número de índice inválido na primeira chamada Patch. O segundo Patch continua apesar deste erro anterior. O primeiro erro é reportado ao utilizador final e apresentado como um erro no Studio no controlo:

Animação a mostra só o segundo registo na tabela T a ser atualizado com números aleatórios depois de cada clique do botão, o primeiro registo resulta num erro

IfError pode ser usado para parar a execução após um erro. À semelhança da função If, o terceiro argumento desta função fornece um local para colocar ações que só devem ser executadas se não houver erro:

Animação a mostrar sem alterações a qualquer um dos registos na tabela T, porque IfError está a impedir que a segunda operação seja concluída depois de um erro

Se for encontrado um erro durante uma das iterações de ForAll, as restantes iterações não irão parar. ForAll é projetado para executar cada iteração de forma independente, permitindo a execução paralela. Quando ForAll estiver concluído, será obtido um erro que contém todos os erros encontrados (examinando AllErrors em IfError ou App.OnError).

Por exemplo, a fórmula seguinte irá resultar em ForAll obter dois erros (para a divisão por zero para Value de 0, duas vezes) e Collection terá três registos (para quando Value não for 0): [1, 2, 3].

Clear( Collection ); 
ForAll( [1,0,2,0,3], If( 1/Value > 0, Collect( Collection, Value ) ) );

Trabalhar com vários erros

Uma vez que uma fórmula de comportamento pode executar mais do que uma ação, também pode encontrar mais do que um erro.

Por predefinição, o primeiro erro é reportado ao utilizador final. Neste exemplo, ambas as chamadas Patch irão falhar, a segunda com um erro de divisão por zero. Só é apresentado o primeiro erro (acerca do índice) ao utilizador:

Primeiro erro de índice apresentado numa faixa de erro, o segundo erro não é reportado

As funções IfError e App.OnError podem aceder a todos os erros encontrados com a variável de âmbito AllErrors. Neste caso, podemos definir esta para uma variável global e ver ambos os erros encontrados. Aparecem na tabela na mesma ordem em que foram encontrados:

Captura dos erros para a variável global PatchErrors, onde podemos ver que ambos os erros estão presentes

Também é possível obter vários erros em fórmulas de não comportamento. Por exemplo, a utilização da função Patch com um lote de registos a atualizar pode obter vários erros, um para cada registo que falha.

Erros em tabelas

Como vimos anteriormente, os erros podem ser armazenados em variáveis. Os erros também podem ser incluídos em estruturas de dados, tais como tabelas. Isto é importante para que um erro em qualquer um dos registos não possa invalidar a tabela completa.

Por exemplo, considere este controlo de tabela de dados no Power Apps:

Tabela de dados a mostrar um erro para o campo Recíproco para uma entrada de 0, o que resulta num erro de divisão por zero

O cálculo em AddColumns encontrou um erro de divisão por zero para um dos valores. Para esse registo, a coluna Recíproca tem um valor de erro (divisão por zero), mas os outros registos não têm e estão bem. IsError( Index( output, 2 ) ) retorna false e IsError( Index( output, 2 ).Value ) retorna true.

Se ocorrer um erro ao filtrar uma tabela, o registo completo é um erro, mas continua a ser obtido no resultado, para que o utilizador final saiba que algo estava lá e ocorreu um problema.

Considere este exemplo. Aqui, a tabela original não tem erros, mas o ato de filtragem cria um erro sempre que o Valor for igual a 0:

Tabela de dados a mostrar erros para dois registos que não foi possível processar pelos critérios de Filtro

Os valores -5 e -3 são removidos do filtro corretamente. Os valores 0 resultam num erro no processamento do filtro, pelo que não é claro se o registo deve ser incluído ou não no resultado. Para maximizar a transparência para os utilizadores finais e ajudar os criadores a depurar, incluímos um registo de erros em vez do original. Neste caso, IsError( Index( output, 2 ) ) obtém true.

Erros de origem de dados

As funções que modificam dados em origens de dados, como Patch, Collect, Remove, RemoveIf, Update, UpdateIf, e SubmitForm reportam erros de duas formas:

  • Cada uma destas funções obterá um valor de erro como resultado da operação. Os erros podem ser detetados com IsError e substituídos ou suprimidos com IfError e App.OnError, como habitualmente.
  • Após a operação, a função Errors também obterá os erros de operações anteriores. Isto pode ser útil para apresentar a mensagem de erro num ecrã de formulário sem precisar de capturar o erro numa variável de estado.

Por exemplo, esta fórmula irá verificar se ocorre um erro de Collect e apresentar uma mensagem de erro personalizada:

IfError( Collect( Names, { Name: "duplicate" } ),
         Notify( $"OOPS: { FirstError.Message }", NotificationType.Warning ) )

A função Errors também obtém informações sobre erros passados durante operações de runtime. Pode ser útil para apresentar um erro num ecrã de formulário sem precisar de capturar o erro numa variável de estado.

Erros de voltar a lançar

Por vezes, alguns erros potenciais são esperados e podem ser ignorados em segurança. Dentro de IfError e App.OnError, se for detetado um erro que deva ser transmitido para o processador superior seguinte, pode ser lançado novamente com Error( AllErrors ).

Criar erros próprios

Também pode criar os seus próprios erros com a função Error.

Se estiver a criar os seus próprios erros, recomenda-se que utilize valores superiores a 1000 para evitar conflitos potenciais com valores de erro de sistema futuros.

Valores de enumeração ErrorKind

Enumeração ErrorKind valor Description
AnalysisError 18 Erro de sistema. Ocorreu um problema com a análise do compilador.
BadLanguageCode 14 Foi utilizado um código de idioma inválido ou não reconhecido.
BadRegex 15 Expressão regular inválida. Verifique a sintaxe utilizada com as funções IsMatch, Match ou MatchAll.
Conflito 6 O registo que está a ser atualizado já foi alterado na origem e o conflito tem de ser resolvido. Uma solução comum é guardar quaisquer alterações locais, atualizar o registo e voltar a aplicar as alterações.
ConstraintViolated 8 O registo não passou uma verificação de restrição no servidor.
CreatePermission 3 O utilizador não tem a permissão para criar registos para a origem de dados. Por exemplo, a função Collect foi chamada.
DeletePermissions 5 O utilizador não tem a permissão para eliminar registos para a origem de dados. Por exemplo, a função Remove foi chamada.
Div0 13 Divisão por zero.
EditPermissions 4 O utilizador não tem a permissão para criar registos para a origem de dados. Por exemplo, a função Patch foi chamada.
GeneratedValue 9 Um valor foi transmitido erroneamente para o servidor para um campo que é calculado automaticamente pelo servidor.
InvalidFunctionUsage 17 Utilização inválida de uma função. Frequentemente, um ou mais dos argumentos para a função estão incorretos ou são utilizados de forma inválida.
FileNotFound 17 Não foi possível encontrar o armazenamento SaveData.
InsufficientMemory 21 Não existe memória ou armazenamento suficiente no dispositivo para a operação.
InvalidArgument 25 Um argumento inválido foi transmitido para uma função.
Interna 26 Erro de sistema. Ocorreu um problema interno com uma das funções.
MissingRequired 2 Um campo obrigatório de um registo estava em falta.
Rede 23 Ocorreu um problema com as comunicações de rede.
None 0 Erro de sistema. Não há nenhum erro.
Não Aplicável 27 Não existem valores disponíveis. Útil para diferenciar um valor em branco que pode ser tratado como um zero em cálculos numéricos, de valores em branco que devem ser sinalizados como um problema potencial se o valor for utilizado.
NotFound 7 Não foi possível encontrar o registo. Por exemplo, o registo a ser modificado na função Patch.
NotSupported 20 Operação não suportada por este leitor ou dispositivo.
Numérico 24 Uma função numérica foi utilizada de forma indevida. Por exemplo, substitua Sqrt por -1.
QuoteExceeded 22 Quota de armazenamento excedida.
ReadOnlyValue 10 A coluna é só de leitura e não pode ser modificada.
ReadPermission 19 O utilizador não tem a permissão de ler registos para a origem de dados.
Sincronizar 5 Foi comunicado um erro pela origem de dados. Verifique a coluna Mensagem para obter mais informações.
Desconhecido 12 Ocorreu um erro, mas de um tipo desconhecido.
Validação 11 O registo não passou uma verificação de validação.