Planejar uma operação assíncrona sem depender de um formulário que pode desaparecer
Artigo · análise e investigação · IntermediárioMétodos anônimos, exceções e distinção entre thread principal e trabalho em segundo plano
Delphi 11.3 e 13.2 como referência; snippets não compilados neste ambienteColocar uma chamada HTTP dentro de uma tarefa pode devolver fluidez à interface. Também pode criar um problema mais difícil: quando a resposta chega, o usuário já fechou a tela ou iniciou outra busca. O trabalho assíncrono precisa de uma regra de duração e de entrega, além de uma thread.
Vamos pensar em uma consulta que recebe um identificador e devolve um texto. O serviço não deve capturar controles visuais. A apresentação decide se o resultado ainda pode ser exibido.
Separe operação e apresentação
Antes de iniciar o trabalho, copie as entradas necessárias na thread da interface. Passe valores para o serviço, em vez de deixar a tarefa ler EditBusca.Text depois.
Interface: captura entrada e cria identificador da operação
Serviço: executa trabalho sem acessar controles
Entrega: retorna resultado ou falha
Interface: verifica se ainda aceita essa operaçãoSe o usuário iniciou uma segunda pesquisa, a primeira resposta pode ter ficado obsoleta. Um identificador crescente ajuda a reconhecer isso, mas ele também precisa de acesso coerente entre threads. Não compartilhe variáveis mutáveis sem um plano de sincronização.
Entenda Queue e Synchronize
TThread.Queue permite encaminhar uma chamada para a thread principal sem bloquear a thread chamadora enquanto aguarda essa execução. Quando chamado pela própria thread principal, pode executar diretamente; ForceQueue é a alternativa para exigir enfileiramento. Documentação de Queue.
Synchronize executa a chamada na thread principal e suspende a thread chamadora durante esse processamento. Essa diferença importa quando há esperas e dependências entre threads. Documentação de Synchronize.
Encaminhar uma chamada à thread principal não garante que o objeto capturado ainda exista. Uma closure que menciona o formulário não o torna automaticamente uma referência segura até a entrega.
Um snippet com uma finalidade limitada
O trecho abaixo mostra apenas o encaminhamento de uma mensagem calculada em uma thread de trabalho. Use System.Classes e chame esse código dentro da operação em segundo plano:
TThread.Queue(nil,
procedure
begin
// Entregar a mensagem a um consumidor com duração garantida.
// Não capturar um formulário que pode ter sido destruído.
end);O comentário é intencional: preencher esse bloco com LabelResultado.Text := ... sem definir a duração da tela esconderia o principal risco. Esse trecho não é um controlador assíncrono pronto.
Defina o contrato de encerramento
Há várias estratégias possíveis. Uma operação pode ser administrada por um controlador que vive mais que a tela; a tela pode se desinscrever antes de ser destruída; ou a navegação pode aguardar um encerramento coordenado. Escolha uma estratégia explícita e teste a corrida entre entrega e fechamento.
Não confunda solicitar cancelamento com interromper imediatamente uma chamada bloqueante. O serviço precisa observar o sinal em pontos apropriados e ter limites de tempo. O timeout de rede faz parte desse desenho.
Evite esperar pela tarefa na thread da interface se a própria tarefa precisa dessa thread para concluir. Essa dependência pode produzir bloqueio mútuo. Também não use Application.ProcessMessages como solução geral: ele pode permitir reentrada em eventos durante uma operação incompleta.
Erros e estados precisam voltar
Uma falha deve encerrar o estado de carregamento da mesma maneira que um sucesso, desde que a operação ainda seja aceita. Diferencie resultado vazio de falha. Apresente uma mensagem útil e registre o diagnóstico sem tokens ou dados pessoais.
Desabilitar um botão durante a consulta é apenas uma escolha de apresentação. A operação ainda deve lidar com saída da tela, interrupção de rede e resposta atrasada.
Um roteiro que encontra as corridas
Teste busca seguida de outra busca, fechamento imediato, timeout, retorno do segundo plano e erro antes da entrega. Repita com demora artificial no serviço. O resultado esperado é que uma resposta antiga não substitua a atual e que não haja acesso a objetos já destruídos.
Não houve execução desses testes em Delphi aqui. Para organizar o tratamento da falha, leia Exceções e recursos.
Operação, entrega e encerramento
- Capture valores e geração; referência ao formulário não garante vida.
- Na entrega, contexto válido confere geração e descarta resposta antiga.
- Atualize UI na thread principal; erro e cancelamento têm estados.
- Ao fechar, invalide geração e remova destino antes da destruição.
- Termine operação depois do fechamento: callback não pode acessar controles destruídos.
TThread.Queue agenda, não prolonga vida. Roteiro/snippets não constituem exemplo integrado testado; build FMX e ensaio de corridas dependem do ambiente visual.
Projeto de estudo disponível
Baixar fonte FMX com .pas e .fmx. Inclui layout rolável, registro de geometria e tarefa local com contexto de entrega e geração. O worker captura valores; o contexto perde o destino antes da destruição e respostas de gerações antigas são descartadas.
Compilado para Win32 com DCC32 37.0 em 08/10/2026. Relatório. Não executado visualmente: testes de dispositivo, geometria/rotação e corridas de encerramento permanecem pendentes. A referência anterior a projeto pendente se refere à integração validada no alvo; este pacote entrega fonte de estudo, sem alegar esse teste.