Asynchronous callouts (also referred to as Apex continuations) allow you to invoke long-running services without tying up server resources and running into Apex concurrency limits. Although Apex continuations are not currently supported natively in the Lightning Component Framework, you can use the custom solution described in this article to make asynchronous Apex callouts from within Lightning Components.
Invoking long-running services with synchronous Apex callouts can block server threads and negatively impact the overall system availability. To ensure that system resources are available to all customers, the Salesforce platform limits the number of concurrent long-running requests per organization: each org is allowed a maximum of ten concurrent requests running for longer than five seconds. When that limit is reached, subsequent requests fail until an existing request completes.
Apex continuations provide an alternative architecture for invoking long-running services in a scalable fashion without blocking server threads and without running into Apex concurrency limits. Apex continuations allow you to make asynchronous callouts to long-running services and provide a callback method that is automatically invoked when the request completes.
Here is a simple example showing how to implement an asynchronous callout using an Apex continuation:
At a high level, this approach works like this:
- The Lightning Component embeds a Visualforce page (in an iframe).
- Using the standard window.postMessage() API, the Lightning Component instructs the iframed Visualforce page to invoke the Apex continuation on its behalf.
- When the request completes, the Visualforce page uses the same window.postMessage() API to pass the result back to its Lightning Component container.
Check out this blog post to learn more about Lightning Component to Visualforce page communication using the window.postMessage() API.
To experiment with invoking Apex continuations from Lightning Components, we will use a sample REST service that allows you to simulate network latency. The source code for this service is available in this repository. The service has two endpoints (you can try these URLs in your browser):
- The following endpoint allows you to retrieve a list of products with a simulated latency (6000 milliseconds in this example):
- The following endpoint allows you to retrieve a specific product (product 1 in this example) with a simulated latency (6000 milliseconds in this example):
Example 1: Simple Continuation
The sample code for the examples below is available in this repository.
In this first example, the SimpleContinuationDemo Lightning component allows you to retrieve a specific product. For simplicity, the result is displayed as raw json.
- The SimpleContinuationDemo Lightning Component embeds the SimpleContinuation Visualforce page in an iframe
- When you click the Get Product button, SimpleContinuationDemo posts a message to the embedded Visualforce page, passing the id of the product to retrieve, and the simulated service latency in milliseconds.
This simple example is a great way to identify all the pieces required for this integration, but it has a number of limitations:
- There are a lot of pieces involved and some low level plumbing to set up. If you need to call an Apex continuation from a different component, you’ll have to set up that infrastructure again.
- The Visualforce base URL (vfBaseURL) and Lightning Component base URL (lcBaseURL) that are required by the window.postMessage() API to ensure the communication is happening between trusted parties are hardcoded.
Example 2: A generic ContinuationProxy component
In this second example, we address the limitations identified in the first example. The low level plumbing and the low level communication infrastructure is encapsulated in a reusable Lightning Component named ContinuationProxy. To call an Apex continuation from a Lightning Component, all you have to do is:
- Drop the ContinuationProxy component in your component:
- Use the proxy to invoke the Apex continuation method like this:
Take a look at the ContinuationProxyDemo component for an example.
Example 3: Continuation broker component
You can use ContinuationProxy in any Lightning Component that needs to invoke Apex continuations. However, if you have multiple Lightning Components that use ContinuationProxy on the same page, they will all have their own iframe, and the proliferation of iframes on a page may lead to some overhead. In that case, you can use a broker component that wraps a single instance of the ContinuationProxy and invokes Apex continuations on behalf of the other components on the page. ContinuationBroker provides an example of such a broker component.
To invoke an Apex continuation using this pattern, a component fires an application event passing the name of the method to invoke, a list of arguments, and a callback method to invoke upon completion of the request. For example, here is how the getProduct() continuation is invoked in ContinuationBrokerDemo:
Supporting clickjack protection
The clickjack protection settings available in Setup (under session settings) allow you to secure your Visualforce pages against user interface redress attacks. Clickjack protection is implemented by adding the X-Frame-Options: SAMEORIGIN header to Visualforce pages, preventing them from being iframed in pages originating from a different domain.
This impacts the approach described in this article because Visualforce pages and Lightning components are served from different domains:
- Lightning components are served from https://your-domain.lightning.force.com
- VF pages are served from https://your-domain–c.naXX.visual.force.com
In other words, if you enable clickjack protections, the X-Frame-Options: SAMEORIGIN header prevents Visualforce pages from being iframed in Lightning Components. The solution is to override the value of the header for the ContinuationProxy page and set it to ALLOW FROM https://xyz-domain.lightning.force.com. This allows the ContinuationProxy page (and only that page) to be embedded in a Lightning Component hosted on your own Lightning domain (and only that domain). You can easily set that header in the constructor of the Visualforce page controller. Here is the ContinuationController constructor as an example:
NOTE: We also set the ‘Content-Security-Policy’, ‘frame-ancestors’ header for wider browser support.
Apex continuations (aka asynchronous callouts) provide a scalable architecture for calling long-running services without blocking server threads and without running into Apex concurrency limits. There is currently no built-in support for continuations in Lightning Components. However, using the approach described in this article, you can use a Visualforce page as a proxy to invoke Apex continuations from Lightning Components.
- Code examples (including the ContinuationProxy component)
- Sample long-running service
- Make Long-Running Callouts from a Visualforce Page
- Prevent Clickjacking Trailhead unit
- Other asynchronous Apex options: Asynchronous Apex Trailhead Module