Comece a acompanhar seu progresso
Página inicial do Trailhead
Página inicial do Trailhead

Usar métodos futuros

Objetivos de aprendizagem

Após concluir esta unidade, você saberá:

  • Quando usar métodos futuros.
  • As limitações para o uso de métodos futuros.
  • Como usar métodos futuros para callouts.
  • Melhores práticas para método futuro.

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. Isto 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 são vencedores com o processamento assíncrono.

Métodos futuros normalmente são utilizados para:
  • 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 de ser usado 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 That Cannot Be Used Together in DML Operations 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.

global 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
  }
}
Nota

Nota

O motivo de os objetos não poderem ser passados como argumentos para métodos futuros é que o objeto pode mudar entre o momento em que o método é chamado e o momento em que ele é realmente executado. Lembre-se: métodos futuros são executados quando os recursos do sistema se tornam disponíveis. Neste caso, o método futuro pode ter um valor de objeto antigo quando for realmente executado, o que pode causar todo tipo de problema.

É importante notar que não é garantido que os métodos futuros são executados na mesma sequência em que foram chamados. Repetimos: não é garantido que os métodos futuros são executados na mesma sequência em que foram chamados. Se você precisar deste tipo de funcionalidade, o Apex que permite a execução em fila pode ser uma solução melhor. 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 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.

Nota

Nota

O código de teste não pode efetivamente enviar callouts para sistemas externos, assim, você terá de “simular” o callout para a cobertura do teste. Verifique o módulo Serviços de integração do Apex quanto a todos os detalhes nos callouts simulados para o teste.

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
global class SMSCalloutMock implements HttpCalloutMock {
    global 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, 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

Como toda invocação de método futuro adiciona uma solicitação à fila assíncrona, evite projetar padrões que adicionem grandes números de solicitações futuras em um curto período de tempo. Se seu projeto tem o potencial de adicionar 2.000 ou mais solicitações de uma vez, as solicitações podem ser atrasadas devido ao controle de fluxo. Aqui estão algumas das melhores práticas que você deve manter em mente:
  • Garanta que os métodos futuros executam 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 futuros são uma ótima ferramenta, mas com grandes poderes vem também uma grande responsabilidade. Aqui estão algumas coisas para manter em mente ao usá-los:
  • Métodos com a anotação de futuro 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() e getContentAsPDF() não podem ser usados em métodos com anotação de futuro.
  • 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.

Recursos

Ícone de flor usado para indicar que o conteúdo é voltado para o Salesforce Classic

Lembre-se: este módulo é destinado ao Salesforce Classic. Quando você iniciar sua organização prática, mude para o Salesforce Classic para concluir este desafio.