Apex-异步

Apex 异步(2)使用future

学习目标

完成这个单位后,你会知道:

  • 何时使用未来的方法。
  • 使用未来方法的局限性。
  • 如何使用未来的标注方法。
  • 未来方法的最佳实践。

Future Apex

未来的Apex用于在单独的线程中运行进程,稍后系统资源可用。

注意:从技术上讲,您使用@future注释来标识异步运行的方法。但是,由于“使用@future注解标识的方法”很费力,通常被称为“将来的方法”,所以我们将在本模块的其余部分中引用它们。

在使用同步处理时,所有的方法调用都是由执行Apex代码的同一个线程来完成的,在这个过程完成之前不会进行额外的处理。你可以使用未来的方法进行任何你想在自己的线程中异步运行的操作。这提供了不阻止用户执行其他操作并为该过程提供更高调控器和执行限制的好处。每个人都是异步处理的赢家。

未来的方法通常用于:

  • 标注到外部Web服务。如果从触发器或执行DML操作之后进行标注,则必须使用将来的或可排队的方法。触发器中的标注将保持数据库连接在标注的生命周期中处于打开状态,这是多租户环境中的“否定”。
  • 如果时间允许,例如某种资源密集型计算或记录处理,您希望在自己的线程中运行的操作。
  • 隔离不同sObject类型的DML操作以防止混合的DML错误。这是一个边缘情况,但你偶尔会遇到这个问题。有关更多详细信息,请参阅无法在DML操作中一起使用的sObjects。

Future方法语法

Future的方法必须是静态方法,并且只能返回一个void类型。指定的参数必须是原始数据类型,原始数据类型数组或原始数据类型的集合。值得注意的是,未来的方法不能将标准或自定义对象作为参数。一个常见的模式是传递一个你想异步处理的记录ID列表。

global class SomeClass {
  @future
  public static void someFutureMethod(List<Id> recordIds) {
    List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds];
    // 处理帐户记录做真棒的东西
  }
}

注意

对象不能作为参数传递给未来方法的原因是因为对象可以在调用方法的时间和实际执行的时间之间改变。请记住,未来的方法在系统资源可用时执行。在这种情况下,未来的方法在实际执行时可能会有一个旧的对象值,这会导致各种不好的事情发生。

重要的是要注意,未来的方法不能保证按照所调用的顺序执行。同样,未来的方法也不能保证按照它们被调用的顺序执行。如果您需要这种类型的功能,那么Queueable Apex可能是更好的解决方案。当使用未来的方法时,也可能会同时运行两个未来的方法,如果两个方法更新相同的记录,可能会导致记录锁定和令人讨厌的运行时错误。

示例标注代码

要为外部服务或API创建Web服务标注,可以使用标记为(callout = true)的未来方法创建Apex类。下面的类具有在不允许标注的情况下同步和异步调用标注的方法。我们在自定义日志对象中插入一条记录来跟踪标注的状态,仅仅是因为日志记录总是很有趣!

public class SMSUtils {

    // 从触发器中调用异步,等等,不允许标注。
    @future(callout=true)
    public static void sendSMSAsync(String fromNbr, String toNbr, String m) {
        String results = sendSMS(fromNbr, toNbr, m);
        System.debug(results);
    }

    // 从控制器等呼叫立即处理
    public static String sendSMS(String fromNbr, String toNbr, String m) {
        // 调用“发送”将会产生一个标注
        String results = SmsMessage.send(fromNbr, toNbr, m);
        insert new SMS_Log__c(to__c=toNbr, from__c=fromNbr, msg__c=results);
        return results;
    }

}

测试类

测试未来的方法与典型的Apex测试有点不同。要测试将来的方法,请将测试代码放在startTest和stopTest测试方法之间。系统收集startTest之后所做的所有异步调用。当执行stopTest时,所有这些收集的异步进程将同步运行。然后您可以断言异步调用正常运行。

注意

测试代码实际上无法将标注发送到外部系统,因此您必须“嘲笑”测试覆盖的标注。查看Apex集成服务模块,获取有关模拟测试标注的完整详细信息。

这是我们用于测试的模拟标注类。 Apex测试框架利用这个“模拟”响应,而不是实际调出REST API端点。

@isTest
global class SMSCalloutMock implements HttpCalloutMock {
    global HttpResponse respond(HttpRequest req) {
        // 创建一个假的回应
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"status":"success"}');
        res.setStatusCode(200);
        return res; 
    }
}
测试类包含一个测试方法,它测试异步和同步方法,因为前者调用后者。
@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();
    // 运行标注并检查结果
    List<SMS_Log__c> logs = [select msg__c from SMS_Log__c];
    System.assertEquals(1, logs.size());
    System.assertEquals('success', logs[0].msg__c);
  }

}

最佳实践

由于每个将来的方法调用都会向异步队列添加一个请求,因此避免在短时间内添加大量未来请求的设计模式。如果您的设计有可能一次添加2000个或更多请求,则由于流量控制,请求可能会延迟。以下是您想要记住的一些最佳做法:

  • 确保将来的方法尽可能快地执行。
  • 如果使用Web服务标注,请尝试将所有标注捆绑在一起,而不是为每个标注使用单独的未来方法。
  • 大规模进行彻底的测试。测试触发排队@future调用的触发器能够处理200条记录的触发器集合。这有助于确定在目前和未来的设计中是否会出现延迟。
  • 考虑使用Batch Apex而不是将来的方法来异步处理大量的记录。这比为每条记录创建未来请求更有效率。

要记住的事情

未来的方法是一个伟大的工具,但拥有巨大的权力是很大的责任。在使用它们时,请注意以下几点:

  • 使用未来注解的方法必须是静态方法,并且只能返回一个void类型。
  • 指定的参数必须是原始数据类型,原始数据类型数组或原始数据类型的集合;未来的方法不能把对象当作论据。
  • 未来的方法将不一定按照它们被调用的顺序执行。另外,有可能两个未来的方法可能同时运行,如果这两个方法正在更新相同的记录,则可能导致记录锁定。
  • 未来的方法不能用于getMethodName(),setMethodName()中的Visualforce控制器中,也不能用于构造器中。
  • 你不能从将来的方法调用未来的方法。在运行未来的方法时,你也不能调用一个调用未来方法的触发器。请参阅参考资料中的链接以防止递归未来方法调用。
  • getContent()和getContentAsPDF()方法不能用于将来注释的方法。
  • 您每个Apex调用将被限制为50个未来呼叫,并且在24小时内对呼叫数量有额外的限制。有关限制的更多信息,请参阅下面的链接。

你可能也会喜欢...