The Lifecycle of a Launch Library

Avid reader Benjamin Bytheway writes:

I’d like to know more about the lifecycle of a Launch library loaded on a page. When the Launch library loads on a page for a customer, what happens with extensions? At what points do certain pieces of code run?

Thanks for writing in, Benjamin.

Let’s discuss the lifecycle of a Launch library. To be clear, this is the library produced from Adobe Experience Platform Launch that gets published to the Launch user’s website.

We’re going to dig into extensions a bit, so it may help to have at least a basic understanding of what a Launch extension can do and how it is composed. If you’re unfamiliar with extension development, I recommend starting out by reading our extension development documentation or watching this video on extension development.

The anatomy of a Launch library

Before digging into the lifecycle, it’s important to understand the library’s anatomy. As discussed previously in Build Optimization Through Minimal Inclusion, it’s important to understand that a Launch library is composed of three parts:

  1. Configuration. This is the information the Launch user provides to Launch while installing extensions, creating rules, creating data elements, etc.
  2. Rule engine. We affectionately call this Turbine and will be referring to this name in the rest of this post. It’s open source and the code can be found here. Its job is to coordinate the operations of the library. It executes rules, caches data element values, sets up the _satellite object, provides debugging utilities, and so on. However, a lot of the functionality of events, conditions, actions, and data elements is delegated to extensions.
  3. Extension library modules. This is the code that extensions provide that detects events, evaluates conditions, performs actions, retrieves data element values, and more. Again, all of this is coordinated by Turbine, the rule engine. Some extensions are written by teams at Adobe, while others are written by third-party developers.

When you publish a library in Launch, the output contains all these pieces bundled together. However, our build system intelligently includes only the pieces of code that may actually be executed while on the websites. Again, feel free to read Build Optimization Through Minimal Inclusion for more information on this optimization.

Alright, now onto the lifecycle.

Preparation of internal helpers

The first thing Turbine does is prepare a handful of internal helpers that are used throughout the lifecycle. A couple examples of helpers are as follows:

  • A helper for data element retrieval, storage, and token replacement (converting data element tokens like %product% to their actual values)
  • A helper for logging (this gives you logging messages prefixed with the 🚀 emoji in the console when debugging is enabled)

Creation of the satellite object

Turbine then creates a global variable that can be found at window._satellite. This object has several methods on it that are useful for consumers to interact programmatically with Launch (for example, toggling debugging) as well as some useful information like the Launch property name, when the library was built, etc.

Retrieval and caching of extension module exports

Extension library modules follow the CommonJS module standard. Just so we can point at things and talk about them more easily, here’s an example of an event type library module called Konami Code Entered. When a visitor to a website enters the Konami Code by hitting certain keys in the correct sequence, any rules using the Konami Code Entered event type will be triggered.

'use strict';

var triggers = [];
var enteredKeys = [];
var konami = '38,38,40,40,37,39,37,39,66,65';

window.addEventListener('keydown', function(event) {
enteredKeys.push(event.keyCode);

while (enteredKeys.length > 10) {
enteredKeys.shift();
}

if (enteredKeys.join(',') === konami) {
triggers.forEach(function(trigger) {
trigger();
});
}
}, true);


module.exports = function(settings, trigger) {
triggers.push(trigger);
};

The next step in the Launch lifecycle is to retrieve and cache extension module exports. In this case, the module export is the function at the bottom (module.exports = function(settings, trigger) { … };). The exported function isn’t run at this point; Turbine is simply retrieving and caching the function. The code that is actually executed at this step in the lifecycle is highlighted in bold:

'use strict';

var triggers = [];
var enteredKeys = [];
var konami = '38,38,40,40,37,39,37,39,66,65';

window.addEventListener('keydown', function(event) {

enteredKeys.push(event.keyCode);

while (enteredKeys.length > 10) {
enteredKeys.shift();
}

if (enteredKeys.join(',') === konami) {
triggers.forEach(function(trigger) {
trigger();
});
}
}, true);


module.exports = function(settings, trigger) {
triggers.push(trigger);
};

This process of retrieving and caching module exports occurs for every library module in every extension that exists in the Launch library. The order in which the modules are processed is indeterminate, though explicit dependencies will be honored. To understand what this means, it’s important to know that a library module can require another library module using a require() statement and a relative path. The following example represents Module A which depends on Module B.

'use strict';
var moduleB = require('./moduleB.js');
module.exports = function(settings, trigger) {
...
}

Now let’s assume Launch begins to process Module A before Module B. At the moment the require('./moduleB.js') statement is reached, Launch will process, cache, and return the exported value from Module B. Launch will then finish processing and caching Module A.

This process applies to any depth of dependencies, so if Module A requires Module B which requires Module C, Module C will finish being processed and its export cached before Module B, and Module B will finish before Module A.

If you’re acquainted with shared modules (a more advanced extension development topic), the same process applies. When a call to turbine.getSharedModule() is reached, Launch will immediately process, cache, and return the exported value from the shared module before proceeding with the rest of the code.

Rule initialization

At this point in time, rules are initialized. When a rule is created by a user in Launch, it is configured with one or more events. For example, a rule might be configured with a Click event so that the rule runs when a user clicks a particular element on the page. The logic that detects when a click occurs would be provided as an event type library module by an extension .

During rule initialization, Turbine iterates over each rule. For each rule, Turbine then iterates over each event. For each event, the settings provided by the Launch user for the event will be passed to the respective event type library module which is provided by its respective extension. For example, if a Launch user had created a rule that uses the Click event type and configured the event to trigger the rule when a website user clicks an element with an ID of checkout-button, the settings containing the ID of checkout-button would be passed to the click event type library module.

Event type library modules then begin monitoring for the respective underlying events to occur on the page.

Rule execution

After rule initialization, Turbine awaits orders from extensions. Any amount of time may pass while awaiting orders. At some point, an extension’s event type library module may detect that the underlying event it is monitoring has occurred (e.g., a user has clicked an element with the ID of checkout-button) and will therefore tell Turbine to run the applicable rule or rules. For each applicable rule, Turbine will then execute the rule’s conditions. If any of the conditions fail, then subsequent conditions and actions for that rule will not be run. If all conditions pass, then Turbine will execute the rule’s actions. While Turbine delegates to extensions for the logic inside individual conditions and actions, it coordinates the overall rule execution process.

This process of waiting until an event occurs, then processing applicable rules, then waiting again until another event occurs, continues until the user eventually navigates away from the page or closes their browser tab.

I hope this has provided some clarity into the Launch library Circle of Life. If you have any questions on specific points in the lifecycle, please let me know in the comments below!