future method cannot be called from a future or batch method
2013-09-02 16:48
525 查看
System.AsyncException: Future method cannot be called from a future
解决方法:Cross-posted at the
Appirio Technology Blog
Governor limits are runtime limits enforced by the Force.com platform to ensure that your code doesn’t, among other things, hog memory resources, lock up the database with an excessive amount of calls or create infinite code loops. Working within governor
limits requires you to sometimes become creative when writing Apex.
One way to work within Force.com platform limits as to use asynchronous Apex methods with the future annotation. Calls to these methods execute asynchronously when the server has available resources and are subject to their own additional limits:
No more than 10 method calls per Apex invocation
No more than 200 method calls per Salesforce.com license per 24 hours
The parameters specified must be primitive dataypes, arrays of primitive datatypes, or collections of primitive datatypes.
Methods with the future annotation cannot take sObjects or objects as arguments.
Methods with the future annotation cannot be used in Visualforce controllers in either getMethodName or setMethodName methods, nor in the constructor.
You cannot call a method annotated with future from a method that also has the future annotation. Nor can you call a trigger from an annotated method that calls another annotated method.
One issue that you can run into when using future methods is writing a trigger with a future method that calls itself recursively. Here is a simple scenario. You have a trigger that inserts/updates a record (or a batch of them) and then makes a future method
call that performs more processing on the same record(s). The issue is that this entire process becomes recursive in nature and you receive the error, “System.AsyncException: Future method cannot be called from a future method…” Here is what it looks
like:
There are a couple of ways to prevent this recursive behavior.
1. Add a new field to the object so the trigger can inspect the record to see if it is being called by the future method.
The trigger checks the value of IsFutureContext__c in the list of Accounts passed into the trigger. If the IsFutureContext__c value is true then the trigger is being called from the future method and the record shouldn’t be processed. If the value of IsFutureContext__c
is false, then the trigger is being called the first time and the future method should be called and passed the Set of unique names.
1 2 3 4 5 6 7 8 9 10 11 12 | trigger ProcessAccount on Account (before insert, before update) { Set<String> uniqueNames = new Set<String>(); for (Account a : Trigger.new) { if (a.IsFutureContext__c) { a.IsFutureContext__c = false; } else { uniqueNames.add(a.UniqueName__c); } } if (!uniqueNames.isEmpty()) AccountProcessor.processAccounts(uniqueNames); } |
the future method once again.
1 2 3 4 5 6 7 8 9 10 11 1213 | public class AccountProcessor { @future public static void processAccounts(Set<String> names) { // list to store the accounts to update List<Account> accountsToUpdate = new List<Account>(); // iterate through the list of accounts to process for (Account a : [Select Id, Name, IsFutureContext__c From Account where UniqueName__c IN :names]) { // ... do you account processing // set the field to true, since we are about to fire the trigger again a.IsFutureContext__c = true; // add the account to the list to update accountsToUpdate.add(a); } // update the accounts update accountsToUpdate; } } |
According to the
Apex docs, static variables are used to store information that is shared within the confines of a class. All instances of the same class share a single copy of the static variable. All triggers that are spawned by the same request can communicate with each
other by referencing static variables in a related class. A recursive trigger can use the value of this class variable to determine when to exit the recursion. I’ve used this method many times before and was pleasantly surprised to find that this class is
also shared when calling a method annotated as future.
The shared ProcessControl class with the static variable that is shared by the trigger and used to determine when to exit the process.
1 2 3 | public class ProcessorControl { public static boolean inFutureContext = false; } |
called and passed the Set of unique names. If the value is true then the trigger is being called from the future method and the records should not be processed.
1 2 34 | trigger ProcessAccount on Account (before insert, before update) { Set<String> uniqueNames = new Set<String>(); if (!ProcessorControl.inFutureContext) { for (Account a : Trigger.new) uniqueNames.add(a.UniqueName__c); if (!uniqueNames.isEmpty()) AccountProcessor.processAccounts(uniqueNames); } } |
1 2 3 4 5 6 7 8 9 10 11 1213 | public class AccountProcessor { @future public static void processAccounts(Set<String> names) { // list to store the accounts to update List<Account> accountsToUpdate = new List<Account>(); // iterate through the list of accounts to process for (Account a : [Select Id, Name From Account where UniqueName__c IN :names]) { // ... do your account processing // add the account to the list to update accountsToUpdate.add(a); } ProcessorControl.inFutureContext = true; // update the accounts update accountsToUpdate; } } |
another package’s future method. You’ll again run into the “System.AsyncException: Future method cannot be called from a future method…” error. What Salesforce needs is an Apex function that determines whether the method is currently executing in a
future call.
Vote for this IdeaISFUTURE Function in APEX |
method based upon whether you are doing an insert or update.
相关文章推荐
- MOSS爬网问题Error from SharePoint site: Data is Null. This method or property cannot be called on Null values
- The method or function that was called cannot be used in the manner requested.
- Data is Null. This method or property cannot be called on Null values.错误分析
- Data is Null. This method or property cannot be called on Null values.
- QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
- MyEclipse10--Cannot return from outside a function or method
- Cannot return from outside a function or method
- 【Eclipse常见错误】-Cannot return from outside a function or method
- Cannot return from outside a function or method
- Cannot return from outside a function or method
- get_transform is not allowed to be called from a MonoBehaviour constructor (or instance field initia
- Cannot return from outside a function or method
- Cannot return from outside a function or method
- myeclipse 10 JSP页面遇到的问题:Cannot return from outside a function or method
- “Cannot return from outside a function or method”
- drp错误集锦---“Cannot return from outside a function or method”
- MyEclipse Multiple annotations found at this line: - Cannot return from outside a function or method
- Cannot return from outside a function or method
- panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
- jsp网页出现“Cannot return from outside a function or method”