3

Update Attempt to Chain in Helper Class -- Not Working -- Separate Class Still Works.

My last update was premature. This doWork method still does not control the order of the calls once they are queued. Still working with this solution.

public with sharing class SandboxRefreshHelper {
   public static void doWork(){
       SandboxDataRefresh.getFullBoxParentCampaigns(); 
       System.enqueueJob(new async1()); 
   }
    
    public class async1 implements Queueable{
        public void execute(QueueableContext con){
            SandboxDataRefresh.getFullBoxChildCampaigns(); 
            System.enqueueJob(new async2());
        }
    }
    public class async2 implements Queueable{
        public void execute(QueueableContext con){
            System.debug('WE ARE DONE'); 
        }
    }
   
}

When I attempt to chain in this way. The future methods are not being called in order. Seems odd, since the documentation explicitly states that this is a purpose of chaining is to chain callouts. So I'm not sure what I'm missing here. However if I put the callout in it's own class entirely, then the chain works as expected.

public with sharing class SandboxRefreshHelper implements Queueable, Database.AllowsCallouts {
public void execute(QueueableContext con){
    System.debug('START CHAIN');
    SandboxDataRefresh.getFullBoxParentCampaigns();  
    System.enqueueJob(new async1()); 
}

public class async1 implements Queueable{
    public void execute(QueueableContext con){
        SandboxDataRefresh.getFullBoxChildCampaigns(); 
        System.enqueueJob(new async2());
    }
}
public class async2 implements Queueable{
    public void execute(QueueableContext con){
        System.debug('WE ARE DONE'); 
    }
}

}

Suppose that I have the following Class

public with sharing class QueueableExample implements Queueable{
    public void execute(QueuableContext con){
             method1(); //future method
             method2(); //future method
             method3(); //future method
   }

}

Question

Is it possible to chain these methods and control their execution without creating multiple classes for each method? In my specific use case, I am doing some basic GET methods that insert records into my salesforce org from another salesforce org.

The only solution I have been able to do is if I have another class setup this way:

My Solution

Create a new class for each method that I want queued up in the chain.

public with sharing class QueuableExample2 implements Queueable{
    public void execute(){
          mySecondMethod()//future
    }
    @future(callout=true)
    public mySecondMethod(){
        //logic to do callout and insert records 
    } 
}
public with sharing class QueueableExample implements Queueable{
    public void execute(QueuableContext con){
             method1(); //future method fires first
             System.enqueueJob(new QueuableExample2()); //future method fires second 
             System.enqueueJob(new QueuableExample3()); //future method fires third
             System.enqueueJob(new QueuableExampleChainPlusOne()); //future method fires n+1 where n = class. 
            
   }

}

1
  • I'd imagine that you could do this with a combo of Queueable and Callable - you'd have chain of jobs to do, each calling a method on a Callable. Would be fund to implement. Jun 15, 2021 at 22:39

3 Answers 3

4

@future is not chainable.

If you need to chain other calls, then you need to implement either the Queueable or Batchable interfaces, and that requires a new class each time.

If you're averse to creating separate apex class files for this, then why not use inner classes?

public class ChainComposer{
    public void doWork(){
        System.enqueueJob(new async1());
    }

    public class async1 implements Queueable{
        public void execute(QueueableContext ctx){
            // do some work
            System.enqueueJob(new async2());
        }
    }

    public class async2 implements Queueable{
        public void execute(QueueableContext ctx){
            // do different work
            System.enqueueJob(new async3());
        }
    }

    public class async3 implements Queueable{
        public void execute(QueueableContext ctx){
            // finalize the work
        }
    }
}

The promise pattern this is not, but it does alleviate the nested chain of "if success, then next call, else note the failure and stop"

The benefit to this pattern is that you still retain the ability to ensure that work X is completed before work Y begins.

Note that queueable execution order is not guaranteed if you try to enqueue everything at once like

// Don't do this
// The order in which parallely enqueued Queueables are run is 
//   not guaranteed
System.enqueueJob(new ChainComposer.async1());
System.enqueueJob(new ChainComposer.async2());
System.enqueueJob(new ChainComposer.async3());

The intent is for you to call doWork(), which would then enqueue async1.
Then when async1 is run, it's final task is to enqueue async2, and so on.

Chaining another queueable at the end of a given queueable is what we need to do to guarantee execution order.

9
  • Not sure that this works with inner classes...The order doesn't seem to be followed with when I follow this design pattern...
    – thinker
    Jun 16, 2021 at 13:48
  • @thinker The intent here is that you would run ChainComposer's doWork(), which would (as its final task) take care of chaining the next queueable call. You can't simply run System.enqueueJob(new ChainComposer.async1()); System.enqueueJob(new ChainComposer.async2()); System.enqueueJob(new ChainComposer.async3()); because Queueables aren't guaranteed to be run in any specific order (and I didn't think @future were run in a guaranteed order either). Going through doWork() would guarantee that the queueables are run in a prescribed order.
    – Derek F
    Jun 16, 2021 at 13:55
  • Correct. I have a Parent Class with Inner Classes that Implement Queueable. Here is the class structure at a high level: Public Parent Class Implements Queuable { public class Child1 Implements Queuable{public void execute (queuableContext con)//do stuff System.enqueJob(new Child2()); } public class Child2 Implements Queuable{} }
    – thinker
    Jun 16, 2021 at 14:19
  • 1
    @thinker I rejected your edit because @future is most definitely not chainable. You cannot call an @future method from within an @future context. Implementing Database.AllowsCallouts is not the same as @future. I have removed the static keyword from my inner classes, as you suggested though (since that part of my example was invalid syntax)
    – Derek F
    Jun 16, 2021 at 15:13
  • 1
    @thinker As per the docs, your understanding is not correct. If you have implements Queueable, Database.AllowsCallouts, then the execute() method (and any code called from the same transaction) can make callouts just fine (no need for a separate @future method to do the callout). I believe you can call an @future from a queueable, but doing so may possibly lead to odd execution ordering.
    – Derek F
    Jun 16, 2021 at 15:58
2

If you want something that looks more Promise-y, you can always implement a Promise-like structure:

public class QueueablePromise implements Queueable, Database.AllowsCallouts {
    public interface Input {
        void input(Object value);
    }
    public interface Output {
        Object output();
    }
    Queueable[] promiseChain = new Queueable[0];
    Object lastResult;
    public QueueablePromise() {
    }
    public QueueablePromise(Queueable job) {
        promiseChain.add(job);
    }
    public void execute(QueueableContext context) {
        Queueable nextIn = promiseChain.remove(0);
        if(nextIn instanceOf Input) {
            Input temp = (Input)nextIn;
            temp.input(lastResult);
        }
        nextIn.execute(context);
        if(nextIn instanceOf Output) {
            Output temp = (Output)nextIn;
            lastResult = temp.output();
        } else {
            lastResult = null;
        }
        if(!promiseChain.isEmpty()) {
            System.enqueueJob(this);
        }
    }
    public QueueablePromise then(Queueable job) {
        promiseChain.add(job);
        return this;
    }
    public Id enqueueJob() {
        return System.enqueueJob(this);
    }
}

And then you implement various queueable types, which can be in one class:

public class QueueableJobs {
  public class QueueableJob1 implements Queueable, QueueablePromise.Output {
    public void execute(QueueableContext context) {
    }
    public Object output() {
      // returns some value
    }
  }
  public class QueueableJob2 implements Queueable, QueueablePromise.Input, QueueablePromise.Output {
    public void input(Object value) {
    }
    public void execute(QueueableContext context) {
    }
    public Object output() {
      // returns some value
    }
  }
  // etc
}

Which can finally be called kind of like a real Promise, as you'd find in JavaScript:

new QueueablePromise(new QueueableJobs.QueueableJob1())
  .then(new QueueableJobs.QueueableJob2())
  .enqueueJob();

This gives you assurance your jobs will be in order, and even arranges for them to store data between transactions if you want.

You could even get more complicated and include a way to implement "catch" as you would in Promises (via Transaction Finalizers). Realistically, the sky's the limit if you want.

1

Here's another implementation of Promise that you can use - this one uses transaction finalizers

https://github.com/codefriar/promisev3

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .