Asynchronous Integration with

Steve Mask

Apr 25, 2019


In a previous post, Overview of Browser Integration with, we looked at how to integrate with using the Browser or JS client synchronously. In the synchronous, or blocking integration, the size of the script is about 25KB after compression. This isn't too large until you have multiple projects running on the same page. In this article, we will look at how to integrate asynchronously, or non-blocking way, for highly latency-sensitive applications and for scenarios where you may be running more than one project on a page at the same time. If you haven't done so already, go to to signup for an account and create your first project.  This will give you the project key needed for the following steps and to begin viewing sessions.  Let's get started!

Integration Options

The table below highlights the pros and cons of each type of integration with  Choose the integration that best meets your needs.

  • Clean and simple integration
  • Configuration only required if running multiple projects simultaneously
  • Latency (50 - 100ms)
  • Size - 96+ KB
Hybrid - Synchronously loads the asynchronous snippet
  • Clean and simple integration
  • Configuration only required if running multiple projects simultaneously
  • Latency (minimal)
  • Size - 1KB
  • Copy/paste JS on page
  • Configuration required
  • Zero latency

Hybrid Integration


Place the following script in the <head> section of your page.

<script src='<PROJECT_KEY>.js'></script>

Asynchronous Integration


Configuration is the same as the synchronous integration but it is necessary to specify your project key here for asynchronous. Simply set an ampConfig object on the window Object as below.

window.ampConfig = {
Make sure to place this before the script tag (See Integration). 

See the Client Integration Document for detailed configuration options.


To integrate asynchronously, just place the following snippet in a script block in the <head> section of your page immediately after the configuration.

"use strict";!function(){window.ampInstanceNames||(window.ampInstanceNames={}),window.ampConfig.instanceName?window.ampInstanceName=window.ampConfig.instanceName:window.ampInstanceName="amp";var e={};if(Object.assign(e,window.ampConfig||{}),window.ampInstanceNames[e.key]=window.ampInstanceName,!window.ampInstanceNames||!window[window.ampInstanceNames[e.key]]){for(var n=window[window.ampInstanceName]={replay:[],config:e,v:"1.2.0",ts:(new Date).getTime()},a="observe decide log".split(" "),t=0;t<a.length;t++)!function(e){n[e]=function(){for(var a=arguments.length,t=Array(a),i=0;i<a;i++)t[i]=arguments[i];"decide"===e&&"function"==typeof t[t.length-1]&&3===t.length&&(t=[t[0],t[1],{},t[2]]),n.replay.push([e,t,(new Date).getTime()])}}(a[t]);var i=document,o=i.getElementsByTagName("script")[0],m=i.createElement("script");o.parentNode.insertBefore(m,o),m.type="text/javascript",m.src=e.scriptSrc||(e.domain||"")+"/libs/"+(e.k... Event("AmpLoaded"));var e=n.config.key,a=window[window.ampInstanceNames[e]];if(a.config=Object.assign(a.config,n.config),a&&n.replay)try{a.config.set("replayTime",(new Date).getTime()-n.ts),n.replay.forEach(function(e){e[2]&&a.timing("requestReplay",{method:name,event:e[0],start:e[2]}).end(),a[e[0]].apply(a,e[1])}),a.config.set("replayTime",0)}catch(e){a.warn(a.error({name:"UNAVAILABLE",message:"Error replaying amp snippet"}))}}}}();

API Calls

Making API calls in an asynchronous integration is the same as with the synchronous integration. The snippet above will setup a replay queue and push any amp.observe and amp.decide calls into it. Once is finished loading, these events will fire off in the order they were received.


Getting a decision back from asynchronously requires a little different implementation than synchronously. In the synchronous integration, explained in the Overview of Browser Integration with blog article, we see that we receive the decision immediately from the call. In an asynchronous integration, we must wait for the decision in the callback function.

amp.decide('EventName', { ctaColor: ['red', 'green', 'blue']}, function(err, decision) {
  // take action on decision returned
  amp.utils.ready('#subscribeBtn', function(element) { = decision.ctaColor;

The code above will send a decide event into a replay queue while is loading. When it has finished loading, all of the events in the queue will fire off in the order they were sent. Once it reaches the above decide event, the callback function will execute immediately with the decision. We may then check if the DOM element, that we want to take action on with the decision, is available. If it is, we set the background color of the button we are querying.  This ensures that we take action as soon as the script has loaded to better our chances of minimizing flicker for above the fold changes.


In this blog we looked at how to integrate with the Browser client asynchronously.  While similar to the synchronous integration, minor changes were needed around making decisions to achieve zero latency.  In the beginning, we spoke about integrating multiple projects at the same time.  In my upcoming blog, we will see how to integrate both synchronously and asynchronously for multiple projects.  This will enable you to run many projects simultaneously to further increase your throughput of tests that you are wanting to run to optimize your application and gain valuable insights for future tests as well.