学习目标
完成这个单位后,你会知道:
- 何时使用Queueable接口。
- 排队和未来方法之间的区别。
- 可排队的Apex语法。
- 可排队方法的最佳实践。
Queueable Apex
Queueable Apex在15年冬季发布,基本上是未来方法的一个超集,并带有一些额外的优势。我们把未来方法的简单性和Batch Apex的力量混合在一起形成了Queueable Apex!它为您提供了平台为您序列化的类结构,简化的界面,无需启动和结束方法,甚至允许您使用不仅仅是原始参数!它由一个简单的System.enqueueJob()方法调用,该方法返回一个可以监视的作业ID。它击败切片面包手!
Queueable Apex允许您提交与未来方法类似的异步处理作业,并具有以下附加优势:
- 非原始类型:您的Queueable类可以包含非原始数据类型的成员变量,如sObjects或自定义Apex类型。这些对象可以在作业执行时被访问。
- 监视:通过调用System.enqueueJob方法提交作业时,该方法将返回AsyncApexJob记录的ID。您可以使用此ID来标识您的作业并监视其进度,可以通过Apex Jobs页面中的Salesforce用户界面,也可以通过从AsyncApexJob查询记录来以编程方式进行监视。
- 链接工作:通过从正在运行的工作中启动第二份工作,可以将一份工作链接到另一份工作上。链接作业是有用的,如果你需要做一些顺序处理。
Queueable与Future
因为可排队的方法在功能上等同于未来的方法,所以大多数情况下你可能希望使用可排队方法而不是将来的方法。但是,这并不意味着您现在应该回头重构所有未来的方法。如果您未来的方法超出了总督限制,或者如果您认为未来的方法需要更高的限制,则可以使用“未来方法更高限制”试验增加未来方法的限制。
使用未来的方法而不是可排队的另一个原因是当你的功能有时被同步执行,有时是异步执行的。以这种方式重构一个方法要比转换成一个可排队的类容易得多。当您发现现有代码的一部分需要移动到异步执行时,这很方便。你可以简单地创建一个相似的将来的方法来包装你的同步方法,就像这样:
@future
static void myFutureMethod(List<String> params) {
// 调用同步方法
mySyncMethod(params);
}
To use Queueable Apex, simply implement the Queueable interface.
public class SomeClass implements Queueable {
public void execute(QueueableContext context) {
// 真棒代码在这里
}
}
示例代码
一个常见的情况是采用一些sObject记录集,执行一些处理,例如向外部REST端点发出调用,或执行一些计算,然后异步地在数据库中更新它们。由于@future方法仅限于原始数据类型(或数组或基元集合),所以可排队的Apex是理想的选择。下面的代码采取帐户记录的集合,为每个记录设置parentId,然后更新数据库中的记录。
public class UpdateParentAccount implements Queueable {
private List<Account> accounts;
private ID parent;
public UpdateParentAccount(List<Account> records, ID id) {
this.accounts = records;
this.parent = id;
}
public void execute(QueueableContext context) {
for (Account account : accounts) {
account.parentId = parent;
// 执行其他处理或标注
}
update accounts;
}
}
// 找到'NY'的所有帐户
List<Account> accounts = [select id from account where billingstate = ‘NY’];
// 为所有记录找到一个特定的父帐户
Id parentId = [select id from account where name = 'ACME Corp'][0].Id;
// 实例化一个Queueable类的新实例
UpdateParentAccount updateJob = new UpdateParentAccount(accounts, parentId);
// 排队处理作业
ID jobID = System.enqueueJob(updateJob);
SELECT Id, Status, NumberOfErrors FROM AsyncApexJob WHERE Id = :jobID
测试Queueable Apex
以下代码示例演示如何在测试方法中测试可排队作业的执行情况。它看起来非常类似于Batch Apex测试。为了确保可排队的进程在测试方法内运行,作业被提交到Test.startTest和Test.stopTest块之间的队列中。在Test.stopTest语句之后,系统同步执行在测试方法中启动的所有异步进程。接下来,测试方法通过查询作业更新的帐户记录来验证可排队作业的结果。
@isTest
public class UpdateParentAccountTest {
@testSetup
static void setup() {
List<Account> accounts = new List<Account>();
// 添加一个父帐户
accounts.add(new Account(name='Parent'));
// 添加100个子帐户
for (Integer i = 0; i < 100; i++) {
accounts.add(new Account(
name='Test Account'+i
));
}
insert accounts;
}
static testmethod void testQueueable() {
// 查询测试数据传递给队列类
Id parentId = [select id from account where name = 'Parent'][0].Id;
List<Account> accounts = [select id, name from account where name like 'Test Account%'];
// 创建我们的Queueable实例
UpdateParentAccount updater = new UpdateParentAccount(accounts, parentId);
// startTest / stopTest block强制异步进程运行
Test.startTest();
System.enqueueJob(updater);
Test.stopTest();
// 验证作业运行。检查记录现在是否有正确的parentId
System.assertEquals(100, [select count() from account where parentId = :parentId]);
}
}
Chaining Jobs
Queueable Apex的最大特点之一就是职位链。如果您需要连续运行作业,Queueable Apex可以让您的生活更轻松。要将作业链接到其他作业,请从可排队类的execute()方法提交第二个作业。您可以从正在执行的作业中仅添加一个作业,这意味着每个父作业只能存在一个子作业。例如,如果您有另一个名为SecondJob的类实现Queueable接口,则可以将此类添加到execute()方法中的队列中,如下所示:
public class FirstJob implements Queueable {
public void execute(QueueableContext context) {
//真棒处理逻辑在这里通过提交下一份工作把这份工作链接到下一份工作
System.enqueueJob(new SecondJob());
}
}
再次,测试有一个稍微不同的模式。您不能在Apex测试中链接可排队的作业,这样做会导致错误。为了避免令人讨厌的错误,您可以通过在链接作业之前调用Test.isRunningTest()来检查Apex是否在测试环境中运行。
要记住的事情
Queueable Apex是一个伟大的新工具,但有几件事值得注意:
- 针对异步Apex方法执行的共享限制,排队作业的执行计数一次。
- 在一个事务中,您可以使用System.enqueueJob将最多50个作业添加到队列中。
- 在链接作业时,只能使用System.enqueueJob从正在执行的作业中添加一个作业,这意味着每个父排队作业只能存在一个子作业。从同一个可排队的作业开始多个子作业是一个禁忌。
- 链式作业的深度没有限制,这意味着你可以链接一个作业到另一个作业,并重复这个过程,每个新的子作业链接到一个新的子作业。但是,对于Developer Edition和Trial orgs,链接作业的最大堆栈深度为5,这意味着您可以链接作业四次,链中的最大作业数为5,包括初始父排队作业。