Skip to main content

Schedule Jobs Using the Apex Scheduler

Learning Objectives

After completing this unit, you’ll know:

  • When to use scheduled Apex.
  • How to monitor scheduled jobs.
  • Scheduled Apex syntax.
  • Scheduled method best practices.

Follow Along with Trail Together

Want to follow along with an instructor as you work through this step? Take a look at this video, which is part of the Trail Together series on Trailhead Live.

(This clip starts at the 1:09:53 minute mark, in case you want to rewind and watch the beginning of the step again.)

Scheduled Apex

The Apex Scheduler lets you delay execution so that you can run Apex classes at a specified time. This is ideal for daily or weekly maintenance tasks using batch Apex. To take advantage of the scheduler, write an Apex class that implements the Schedulable interface, and then schedule it for execution on a specific schedule.

Scheduled Apex Syntax

To invoke Apex classes to run at specific times, first implement the Schedulable interface for the class. Then, schedule an instance of the class to run at a specific time using the System.schedule() method.

public with sharing class SomeClass implements Schedulable {
    public void execute(SchedulableContext ctx) {
        // awesome code here
    }
}

The class implements the Schedulable interface and must implement the only method that this interface contains, which is the execute() method.

The parameter of this method is a SchedulableContext object. After a class has been scheduled, a CronTrigger object is created that represents the scheduled job. It provides a getTriggerId() method that returns the ID of a CronTrigger API object.

Sample Code

This class queries for open opportunities that should have closed by the current date, and creates a task on each one to remind the owner to update the opportunity.

public with sharing class RemindOpptyOwners implements Schedulable {
  public void execute(SchedulableContext ctx) {
    List<Opportunity> opptys = [
      SELECT Id, Name, OwnerId, CloseDate
      FROM Opportunity
      WHERE IsClosed = FALSE AND CloseDate < TODAY
      WITH USER_MODE
    ];
    // Create a task for each opportunity in the list
    List<Task> tasks = new List<Task>();
    for (Opportunity opp : opptys) {
      Task newTask = new Task(
        Subject = 'Update the Opportunity!',
        Priority = 'Normal',
        Status = 'Not Started',
        WhatId = opp.Id
      );
      tasks.add(newTask);
    }
    insert as user tasks;
  }
}



You can schedule your class to run either programmatically or from the Apex Scheduler UI.

Using the System.Schedule Method

After you implement a class with the Schedulable interface, use the System.schedule() method to execute it. The System.schedule() method uses the user’s timezone for the basis of all schedules.

Note

Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled job classes than the limit. In particular, consider API bulk updates, import wizards, mass record changes through the user interface, and all cases where more than one record can be updated at a time.

The System.schedule() method takes three arguments: a name for the job, a CRON expression used to represent the time and date the job is scheduled to run, and an instance of a class that implements the Schedulable interface.

RemindOpptyOwners reminder = new RemindOpptyOwners();
// Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
String sch = '20 30 8 10 2 ?';
String jobID = System.schedule('Remind Opp Owners', sch, reminder);

For more information on the CRON expression used for scheduling, see the “Using the System.Schedule Method” section in Apex Scheduler.

Scheduling a Job from the UI

You can also schedule a class using the user interface.

  1. From Setup, enter Jobs in the Quick Find box, then select Scheduled Jobs.
  2. Click Schedule Apex.
  3. For the job name, enter something like Daily Oppty Reminder.
  4. Click the lookup button next to Apex class and enter * for the search term to get a list of all classes that can be scheduled. In the search results, click the name of your scheduled class.
  5. Select Weekly or Monthly for the frequency and set the frequency desired.
  6. Select the start and end dates, and a preferred start time.
  7. Click Save.

Testing Scheduled Apex

Just like with the other asynchronous Apex methods we’ve covered so far, with scheduled Apex you must also ensure that the scheduled job is finished before testing against the results. To do this, use startTest() and stopTest() again around the System.schedule() method to ensure processing finishes before continuing your test.

@IsTest
private with sharing class RemindOppyOwnersTest {
  // Placeholder CRON expression: midnight on March 15.
  // Because this is a test, job executes
  // immediately after Test.stopTest(), not at the time set in the CRON expression
  public static String CRON_EXP = '0 0 0 15 3 ? 2042';


  @IsTest
  static void testScheduledJob() {
    // Create some out-of-date Opportunity records
    List<Opportunity> opptys = new List<Opportunity>();
    Date closeDate = Date.today().addDays(-7);
    for (Integer i = 0; i < 10; i++) {
      Opportunity o = new Opportunity(
        Name = 'Opportunity ' + i,
        CloseDate = closeDate,
        StageName = 'Prospecting'
      );
      opptys.add(o);
    }
    insert as user opptys;


    // Get the IDs of the opportunities we just inserted
    Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>(opptys);
    List<Id> opptyIds = new List<Id>(opptyMap.keySet());
    Test.startTest();
    // Schedule the test job
    String jobId = System.schedule(
      'ScheduledApexTest',
      CRON_EXP,
      new RemindOpptyOwners()
    );
    // Verify the scheduled job has not run yet.
    List<Task> lt = [
      SELECT Id
      FROM Task
      WHERE WhatId IN :opptyIds
      WITH USER_MODE
    ];
    Assert.areEqual(0, lt.size(), 'Tasks exist before job has run');
    // Stopping the test will run the job synchronously
    Test.stopTest();
    // Now that the scheduled job has executed,
    // check that our tasks were created
    lt = [
      SELECT Id
      FROM Task
      WHERE WhatId IN :opptyIds
      WITH USER_MODE
    ];
    Assert.areEqual(opptyIds.size(), lt.size(), 'Tasks were not created');


    // Check the scheduled time
    List<CronTrigger> ct = [
      SELECT Id, TimesTriggered, NextFireTime
      FROM CronTrigger
      WHERE Id = :jobId
      WITH USER_MODE
    ];
    System.debug('Next Fire Time ' + ct[0].NextFireTime);
  }
}

Things to Remember

Scheduled Apex has a number of items you need to be aware of (see Apex Scheduler for a complete list when you have time), but in general:

  • You can only have 100 scheduled Apex jobs at one time, and there are a maximum number of scheduled Apex executions per a 24-hour period. See Execution Governors and Limits for details.
  • Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled jobs than the limit.
  • Synchronous web service callouts are not supported from scheduled Apex. To be able to make callouts, make an asynchronous callout by placing the callout in a method annotated with @Future(callout=true) and call this method from scheduled Apex. However, if your scheduled Apex executes a batch job, callouts are supported from the batch class.

Resources

在 Salesforce 帮助中分享 Trailhead 反馈

我们很想听听您使用 Trailhead 的经验——您现在可以随时从 Salesforce 帮助网站访问新的反馈表单。

了解更多 继续分享反馈