Event System
Internal pub/sub event system for SDK lifecycle events
In ApiManager, we saw how the SDK acts like a messenger, talking to the Convert servers to fetch configuration data and send back tracking reports. This handles communication with the outside world.
But how do different parts inside the SDK talk to each other? For example, when the configuration data is fetched successfully, how does the rest of the SDK know "the data is here, we're ready to go!"?
The Problem: Internal Announcements
Imagine a large office building (the SDK). Different departments (the Managers like DataManager, ApiManager, etc.) are working on their tasks. When one department finishes something important, how do they notify other departments that might need to know?
- The Mail Room (ApiManager) receives a big package (the project configuration). How does it announce this arrival?
- The Decision Desk (BucketingManager) assigns someone to a specific project (buckets a visitor). How can the Logging Department know about this decision to record it?
- The Front Desk (Core) needs to know when the entire office is officially open for business (SDK is initialized and ready).
They need a structured way to make announcements and for interested departments to listen to those announcements.
What is EventManager? The Office Intercom System
EventManager? The Office Intercom SystemMeet the EventManager! Think of it as the internal intercom or announcement system for the Convert SDK. It allows different parts of the SDK to broadcast messages (events) and other parts (or even you, the developer) to listen for specific messages.
It works on a simple Publish/Subscribe (or "Pub/Sub") model:
-
Publishing (Firing Events): When something significant happens, a component uses the
EventManagertofirean event. This is like making an announcement over the intercom. For example: -
Subscribing (Listening with
on): Other components (or your code) can register interest in specific events using theonmethod. When that event fires, the listener's callback function is executed.
This system allows different parts of the SDK to communicate and react to happenings without being tightly coupled or needing direct references to each other.
How it's Used
1. The onReady() Convenience Method:
The most common way you'll interact with the EventManager's work is indirectly, through the SDK's onReady() method (see Core).
When you create an SDK instance, it starts initializing. Once the initial configuration data is fetched and processed, the Core module uses the EventManager to fire the Ready event. The onReady() method had previously set up a listener for this specific event. When the Ready event is fired, the listener's callback runs, which resolves the readiness signal, allowing your application code to proceed.
2. Subscribing to SDK Events:
You can listen for internal events by calling on(eventName, callbackFunction) on the SDK instance (which wraps the EventManager's on method).
- The first argument is the event name, typically from the
SystemEventsenumeration. - The second argument is a callback function that receives event-related data and potentially an error object.
- When the event fires, all registered callbacks for that event are executed.
For example, you can subscribe to the Bucketing event to be notified whenever a visitor is assigned to a variation, or subscribe to the Conversion event to react when a conversion is tracked. This allows for advanced use cases, like sending SDK event data to your own analytics system or debugging internal SDK behaviour.
3. Available Events:
The SDK uses the SystemEvents enumeration for event names:
| Event | Value | Triggered By |
|---|---|---|
Ready | 'ready' | SDK initialization complete |
ConfigUpdated | 'config.updated' | Configuration refreshed |
Bucketing | 'bucketing' | Visitor bucketed into a variation |
Conversion | 'conversion' | Conversion tracked |
Segments | 'segments' | Segments evaluated |
LocationActivated | 'location.activated' | Location rules matched |
LocationDeactivated | 'location.deactivated' | Location rules no longer matched |
Audiences | 'audiences' | Audience rules evaluated |
ApiQueueReleased | 'api.queue.released' | Tracking queue flushed |
Under the Hood: The Intercom Mechanism
How does the EventManager manage announcements and listeners?
1. Keeping Lists (listeners):
The EventManager maintains an internal record (a map/dictionary). The keys are event names (e.g., 'ready', 'bucketing'), and the value for each key is a list of all the callback functions that have subscribed to that event.
// Internal state of listeners
{
'ready': [callback1, callback2],
'bucketing': [callbackA],
'conversion': [callbackX, callbackY],
}
2. Subscribing (on):
When on('eventName', callbackFn) is called, it finds the list for 'eventName' in the listeners record (or creates it if it doesn't exist) and adds callbackFn to that list.
3. Firing (fire):
When some code calls fire('eventName', args, error), the EventManager:
- Looks up
'eventName'in its listeners record. - If it finds a list of listeners for that event:
- It iterates through each callback function in the list.
- It calls each function, passing the
argsanderrorobject to it.
- If no listeners are found for
'eventName', it does nothing.
4. Deferred Events (like Ready):
What if you register a listener for the Ready event after the SDK has already initialized and fired it? You still want your callback to run!
The EventManager has a mechanism for this using the deferred flag in the fire method.
- When
fire('ready', ..., deferred: true)is called, besides notifying current listeners, it also stores the fact that the'ready'event has happened (in another internal record calleddeferred). - Later, when
on('ready', callbackFn)is called, theonmethod checks the deferred record. If it sees that'ready'has already fired, it immediately executes thecallbackFnwith the stored arguments.
This ensures that listeners added after a deferred event has fired still get notified.
Sequence Diagram: onReady Flow
sequenceDiagram
participant UserCode as Your Code
participant Core as ConvertSDK / Core
participant EventMgr as EventManager
participant ApiMgr as ApiManager
UserCode->>Core: new ConvertSDK()
UserCode->>Core: onReady()
Core->>+EventMgr: on('ready', callbackForPromise)
EventMgr-->>-Core: Listener registered
Core->>+ApiMgr: getConfig()
Note right of Core: SDK Initialization starts...
ApiMgr-->>Core: Returns Config Data
Core->>Core: Process data, setup complete.
Core->>+EventMgr: fire('ready', null, null, true) // Fire READY event (deferred)
Note right of EventMgr: Found listener 'callbackForPromise'
EventMgr->>Core: Execute callbackForPromise()
Core->>UserCode: Resolve the onReady() Promise
EventMgr-->>-Core:
UserCode-->>UserCode: .then() block runs
Sequence Diagram: Deferred Event (listener registered after fire)
sequenceDiagram
participant UserCode as Your Code
participant Core as Core
participant EventMgr as EventManager
UserCode->>Core: ConvertSDK::create()
Core->>Core: Initialize, fetch config...
Core->>+EventMgr: fire('ready', null, null, deferred: true)
Note right of EventMgr: No listeners yet — store as deferred
EventMgr-->>-Core: Done
Core-->>UserCode: Return Core instance
UserCode->>+EventMgr: on('ready', callback)
Note right of EventMgr: 'ready' already fired (deferred) — execute immediately
EventMgr->>UserCode: Execute callback()
EventMgr-->>-UserCode: Done
Implementation Details
The EventManager implementation consists of three main elements:
1. Internal State:
The EventManager initializes with:
- A
listenersmap -- stores arrays of callbacks keyed by event name. - A
deferredmap -- stores events that fired before listeners were registered, along with their arguments and error data. - An optional
mapperfunction -- transforms event arguments before passing them to listeners (defaults to identity/passthrough).
2. The on Method:
When a listener is registered:
- It ensures an array exists for the given event name in the listeners map.
- It appends the callback to that array.
- It checks the deferred store -- if the event already fired as deferred, it immediately calls
fireagain so the new listener is notified with the stored arguments.
3. The fire Method:
When an event is fired:
- It retrieves the list of listener functions for the given event.
- It loops through the list and calls each function, passing the (optionally mapper-transformed) arguments and error.
- Each listener call is wrapped in error handling so a failing listener does not crash the SDK.
- If the
deferredflag is set totrueand the event has not been stored already, it saves the event arguments in the deferred store for future late-subscribing listeners.
4. The removeListeners Method:
Removes all registered listeners for a given event name.
Conclusion
The EventManager is the SDK's internal communication backbone, implementing a simple publish/subscribe pattern. It allows different components to signal important occurrences (fire) and others to react to them (on) without direct dependencies.
Key takeaways:
- Why an internal event system is needed for communication between SDK modules.
- The "intercom" or "pub/sub" analogy for
EventManager. - How components
fireevents (likeReady,Bucketing,Conversion). - How components (or your code) can listen using
on. - How deferred events ensure listeners added late are still notified (important for
Ready). - The
SystemEventsenumeration provides predefined names for all standard SDK events.
Updated about 1 month ago