Usar métodos futuros
Objetivos de aprendizagem
Acompanhar com o Trail Together
Deseja acompanhar um instrutor enquanto trabalha nesta etapa? Veja este vídeo que faz parte da série Trail Together no Trailhead Live.
(Este clipe começa na marca dos 09:39 minutos, caso você queira retroceder e ver o início da etapa novamente.)
Apex futuro
O Apex futuro é usado para executar processos em um thread separado, mais tarde, quando os recursos do sistema se tornarem disponíveis.
Nota: Tecnicamente, você usa a anotação @future
para identificar métodos que são executados de forma assíncrona. Porém, como os “métodos identificados com a anotação @future
” são trabalhosos, eles normalmente são citados como “métodos futuros” e é assim que nos referiremos a eles daqui em diante.
Ao usar o processamento sincronizado, todas as chamadas de método são feitas a partir do mesmo thread que está executando o código do Apex e nenhum processamento adicional pode ocorrer até que o processo seja concluído. Você pode usar métodos futuros para qualquer operação que quiser para executar de forma assíncrona em seu próprio thread. Isso oferece os benefícios de não impedir que o usuário realize outras operações e ofereça limites de administrador e de execução maiores para o processo. Todos saem vencedores com o processamento assíncrono.
- Fazer callouts para serviços da Web externos. Se você estiver fazendo callouts a partir de um acionador ou após realizar uma operação DML, tem que usar um método futuro ou que permita a execução em fila. Um callout em um acionador manteria a conexão com o banco de dados aberta pela duração do callout e isso é inaceitável em um ambiente multilocatário.
- Operações que você deseja executar em seu próprio thread, quando o tempo permitir, como um tipo de cálculo ou processamento de registros com consumo intenso de recursos.
- Isolar operações DML em diferentes tipos de sObject para evitar o erro de DML combinada. Isto é um caso extremo, mas você pode passar por este problema. Consulte sObjects que não podem ser usados juntos em operações DML para obter mais detalhes.
Sintaxe do método futuro
Os métodos futuros têm de ser métodos estáticos e só podem retornar um tipo void. Os parâmetros especificados precisam ser tipos de dados primitivos, matrizes de tipos de dados primitivos ou coleções de tipos de dados primitivos. Em especial, métodos futuros não podem lidar com objetos padrão ou personalizados como argumentos. Um padrão comum é passar ao método uma Lista
de IDs de registros que você deseja processar de forma assíncrona.
public class SomeClass { @future public static void someFutureMethod(List<Id> recordIds) { List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds]; // process account records to do awesome stuff } }
É importante notar que não é garantido que os métodos futuros são executados na mesma sequência em que foram chamados. Vamos repetir porque é crucial que você se lembre: não é garantido que métodos futuros sejam executados na mesma sequência em que foram chamados. Ao usar métodos futuros, também é possível que dois métodos futuros possam ser executados simultaneamente, o que poderia resultar em bloqueio de registro e em um desagradável erro de tempo de execução se os dois métodos estiverem atualizando o mesmo registro.
Amostra de código de callout
Para fazer um callout de um serviço da Web para um serviço ou API, crie uma classe de Apex com um método futuro marcado com (callout=true)
. A classe abaixo tem métodos para fazer callout de forma síncrona e assíncrona onde callouts não são permitidos. Inserimos um registro em um objeto de registro personalizado para monitorar o status do callout simplesmente porque sempre é divertido fazer o registro!
public class SMSUtils { // Call async from triggers, etc, where callouts are not permitted. @future(callout=true) public static void sendSMSAsync(String fromNbr, String toNbr, String m) { String results = sendSMS(fromNbr, toNbr, m); System.debug(results); } // Call from controllers, etc, for immediate processing public static String sendSMS(String fromNbr, String toNbr, String m) { // Calling 'send' will result in a callout String results = SmsMessage.send(fromNbr, toNbr, m); insert new SMS_Log__c(to__c=toNbr, from__c=fromNbr, msg__c=results); return results; } }
Classes de teste
Testar métodos futuros é um pouco diferente do teste de Apex típico. Para testar métodos futuros, inclua seu código de teste entre os métodos de teste startTest()
e stopTest()
. O sistema coleta todas as chamadas assíncronas feitas após o startTest()
. Quando o método stopTest()
é executado, todos estes processos assíncronos coletados são executados de forma síncrona. Então é possível afirmar se a chamada assíncrona operou corretamente.
Aqui está nossa classe de callout simulado usado para o teste. A estrutura de teste do Apex usa essa resposta “simulada” em vez de fazer o callout real para o ponto de extremidade da API REST.
@isTest public class SMSCalloutMock implements HttpCalloutMock { public HttpResponse respond(HttpRequest req) { // Create a fake response HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setBody('{"status":"success"}'); res.setStatusCode(200); return res; } }
O teste de classe contém apenas um método de teste (testSendSms()
neste exemplo), que testa ambos os métodos assíncrono e síncrono à medida que o primeiro chama este último.
@IsTest private class Test_SMSUtils { @IsTest private static void testSendSms() { Test.setMock(HttpCalloutMock.class, new SMSCalloutMock()); Test.startTest(); SMSUtils.sendSMSAsync('111', '222', 'Greetings!'); Test.stopTest(); // runs callout and check results List<SMS_Log__c> logs = [select msg__c from SMS_Log__c]; System.assertEquals(1, logs.size()); System.assertEquals('success', logs[0].msg__c); } }
Melhores práticas
- Verifique se os métodos futuros estão sendo executados o mais rápido possível.
- Se você estiver usando callouts de serviço da Web, tente agrupar todos os callouts do mesmo método futuro, em vez de usar um método futuro separado para cada callout.
- Realize testes completos na escala. Teste se um acionador que coloca na fila as chamadas
@future
é capaz de lidar com uma coleção de acionadores de 200 registros. Isto ajuda a determinar se podem ocorrer atrasos devido ao projeto nos volumes atuais e futuros. - Considere usar o Apex de lote em vez de métodos futuros para processar um grande número de registros de forma assíncrona. Isto é mais eficiente que criar uma solicitação de futuro para cada registro.
Coisas a serem lembradas
- Métodos com a anotação
@future
têm de ser métodos estáticos e só podem retornar um tipo void. - Os parâmetros especificados têm de ser tipos de dados primitivos, matrizes de tipos de dados primitivos ou coleções de tipos de dados primitivos. Métodos futuros não podem ter objetos como argumentos.
- Métodos futuros não são executados necessariamente na mesma ordem em que são chamados. Além disso, é possível que dois métodos futuros sejam executados simultaneamente, o que poderia resultar em bloqueio de registro se os dois métodos estivessem atualizando o mesmo registro.
- Os métodos futuros não podem ser usados com controladores Visualforce em
getMethodName()
,setMethodName()
nem no construtor. - Não é possível chamar um método futuro a partir de um método futuro. Também não é possível invocar um acionador que chame um método futuro enquanto executa um método futuro. Veja o link em Recursos para evitar chamadas recursivas de método futuro.
- Os métodos
getContent()
egetContentAsPDF()
não podem ser usados em métodos com anotação@future
. - Há uma limitação de 50 chamadas futuras por invocação Apex e um limite adicional no número de chamadas em um período de 24 horas. Para mais informações sobre limites, acesse o link abaixo.