Managing the Context Lifecycle
If you're building a single-page app and just getting started, you can skip this page. The SDK handles event batching and sending automatically. Come back here when you need to control the timing of event delivery or manage long-lived contexts.
Most of the time the SDK handles event delivery and cleanup automatically. But there are situations where you need to take control: flushing events before a page navigation, closing a context when a user logs out, or refreshing experiment data in a long-running process.
Flushing events with publish
Most SDKs batch events and send them automatically after a short delay. You don't need
to call publish() for typical use cases.
The Javascript SDK running in Node.js, the PHP SDK, and the Ruby SDK do not
auto-publish events. The typical pattern is to create a context at the beginning of each
request, use it throughout the request to check treatments and track goals, and then call
publish() or finalize() at the end before sending the response. This ensures all
events are sent and the context is cleaned up before the request ends.
Even in SDKs with auto-publishing enabled, there are times when you need to make sure events are sent right now, before moving on:
- Before a server sends its response. In server-side rendering, the context might
get garbage collected before the auto-flush fires. Call
publish()before sending the response to make sure exposure and goal data gets recorded. - Before a page navigation. In a traditional multi-page app, navigating away can
interrupt the SDK's flush cycle. Call
publish()beforewindow.locationchanges. - Before a critical business event. If you need confirmation that goal data was recorded before proceeding (e.g., before showing an order confirmation), flush first.
You probably don't need publish() if:
- You're in a single-page app where the context lives for the whole session
- You're calling
finalize()at the end anyway (it flushes automatically) - You're just tracking goals in a normal flow and the SDK's auto-flush is fine
Call publish() before page navigations, server responses, or any point where
the process might exit before the SDK's auto-flush fires. This ensures exposure
and goal data is sent to the collector before the context is lost.
- Javascript
- Python3
- React
- Swift
- Vue2
- Vue3
- Java
- Go
- .NET
- PHP
- Ruby
- Flutter/Dart
// You can just publish
context.publish();
// or wait for it to finish, so if you want to
// navigate to another page without losing impressions,
// you can do that once the promise resolves.
context.publish().then(function () {
document.location.replace("another_page");
});
Python also provides publish_async() for non-blocking use.
context.publish()
const { context } = useABSmartly();
context.publish().then(() => {
document.location.replace("another_page");
});
context.publish().done {
print("all pending events published")
}
await this.$absmartly.publish().then(() => {
window.location = "https://www.absmartly.com";
});
await this.$absmartly.publish().then(() => {
window.location = "https://www.absmartly.com";
});
Java also provides publishAsync() for non-blocking use.
context.publish();
Go also provides PublishAsync() for non-blocking use.
context.Publish()
.NET also provides PublishAsync() for non-blocking use.
context.Publish();
$context->publish();
context.publish
context.publish();
Closing a context with finalize
Finalizing (or closing) a context does two things: it flushes any remaining events
(like publish()), and then it "seals" the context so no more events can be recorded.
Any track() or publish() calls after finalize will throw an error. Calls to
treatment() and variableValue() still return the correct values (assignments are
computed locally), but no new exposures will be recorded.
You should finalize a context when:
- A user logs out. You no longer know if the current user is the same person. Close
the old context and create a new one with an
anonymous_id. - Your application is shutting down. Whether it's a server process exiting, a mobile app going to background, or a component unmounting, finalize ensures nothing gets lost.
- A server-side request is complete. Each request should create and finalize its own context so events are flushed promptly.
Finalizing (or closing) a context flushes any unpublished events and then
seals it. Any track() calls after finalization will throw an error.
- Javascript
- Python3
- React
- Swift
- Vue2
- Vue3
- Java
- Go
- .NET
- PHP
- Ruby
- Flutter/Dart
Call the context.finalize() method before letting a process using the SDK exit, as
this method gracefully shuts down the SDK by clearing caches, and closing
connections. It will also call context.publish() to flush the remaining
unpublished impressions.
// You can just finalize and remove the variable reference
context.finalize();
context = null;
// finalize() returns a promise, so if you want to
// navigate to another page without losing impressions, you
// can do that once the promise resolves.
context.finalize().then(function () {
context = null;
document.location.replace("another_page");
});
After finalize() is called and finishes, any subsequent invocations to the
treatment() method will still return the right variant, but will result in a
warning to let you know that you might be losing impressions.
The close() and close_async() methods will ensure all events have been
published to the ABsmartly collector, like publish(), and will also "seal"
the context, throwing an error if any method that could generate an event is
called.
context.close()
Call the context.finalize() method before letting a process using the SDK exit, as
this method gracefully shuts down the SDK by clearing caches, and closing
connections. It will also call context.publish() to flush the remaining
unpublished impressions.
const { context } = useABSmartly();
context.finalize().then(() => {
document.location.replace("another_page");
});
After finalize() is called and finishes, any subsequent invocations to the
treatment() method will still return the right variant, but will result in a
warning to let you know that you might be losing impressions.
The close() methods will ensure all events have been published to the
ABsmartly collector, like publish(), and will also "seal" the context,
throwing an error if any method that could generate an event is called.
context.close().done {
print("context closed")
}
The finalize() method will ensure all events have been published to the
ABsmartly collector, like publish(), and will also "seal" the context,
throwing an error if any method that could generate an event is called.
await this.$absmartly.finalize().then(() => {
window.location = "https://www.absmartly.com";
});
The finalize() method will ensure all events have been published to the
ABsmartly collector, like publish(), and will also "seal" the context,
throwing an error if any method that could generate an event is called.
await this.$absmartly.finalize().then(() => {
window.location = "https://www.absmartly.com";
});
The close() and closeAsync() methods will ensure all events have been
published to the ABsmartly collector, like publish(), and will also "seal"
the context, throwing an error if any method that could generate an event is
called.
context.close();
The Close() and CloseAsync() methods will ensure all events have been
published to the ABsmartly collector, like Publish(), and will also "seal"
the context, throwing an error if any method that could generate an event is
called.
context.Close();
The ABsmartly Context implements IDisposable and IAsyncDisposable interfaces to ensure
that all events have been published to the ABsmartly collector, like Publish(),
and the context has been "sealed". An error will then be thrown if any method that
could generate an event is called.
To implement this, instead of calling Publish() directly, the using pattern can be used.
using var context = _abSdk.CreateContext(config);
The close() method will ensure all events have been published to the
ABsmartly collector, like Context->publish(), and will also "seal" the context,
throwing an error if any method that could generate an event is called.
$context->close();
The close() method will ensure all events have been published to the
ABsmartly collector, like publish(), and will also "seal" the context, throwing
an error if any method that could generate an event is called.
context.close
The close() method will ensure all events have been published to the
ABsmartly collector, like publish(), and will also "seal" the context, throwing
an error if any method that could generate an event is called.
context.close();
Adding units after context creation
Sometimes you don't have all of a user's identifiers when you first create the context.
For example, you might create a context with an anonymous_id and later learn the
user_id when they log in. You can add units to an existing context without creating
a new one.
- Javascript
- Python3
- React
- Vue2
- Vue3
- Swift
- Java
- Go
- .NET
- PHP
- Ruby
- Flutter/Dart
context.unit("db_user_id", "1000013");
context.units({
db_user_id: "1000013",
});
context.set_unit("db_user_id", "1000013")
context.set_units({
"db_user_id": "1000013"
})
const { context } = useABSmartly();
context.unit("db_user_id", "1000013");
In React, if the user's identity changes entirely (for example, they log out and a
different user logs in), use resetContext() to replace the context with a fresh one:
const { resetContext } = useABSmartly();
resetContext({
units: {
anonymous_id: "new-anonymous-id",
},
});
this.$absmartly.unit("db_user_id", "1000013");
this.$absmartly.units({
db_user_id: "1000013",
});
this.$absmartly.unit("db_user_id", "1000013");
this.$absmartly.units({
db_user_id: "1000013",
});
context.setUnit(unitType: "db_user_id", uid: "1000013")
context.setUnits([
"db_user_id": "1000013"
])
context.setUnit("db_user_id", "1000013");
context.setUnits(Map.of(
"db_user_id", "1000013"
));
context.SetUnit("db_user_id", "1000013")
context.SetUnits(map[string]string{
"db_user_id": "1000013",
})
context.SetUnit("db_user_id", "1000013");
context.SetUnits(new Dictionary<string, string>() {
{ "db_user_id", "1000013" }
});
$context->setUnit('db_user_id', '1000013');
context.set_unit("db_user_id", "1000013")
context.setUnit("db_user_id", "1000013");
context.setUnits({
"db_user_id": "1000013"
});
You cannot override a unit type that has already been set. Setting user_id twice
with different values is a change of identity and will throw an exception. If the user's
identity changes (for example, they log out), finalize the old context and create a new
one.
Refreshing experiment data
When you create a context, the SDK fetches all running experiments and caches them in memory. But if your application runs for a long time (like a Node.js server or a single-page app that stays open all day), new experiments started after the context was created won't be picked up.
There are two ways to handle this:
Automatic refresh: Set a refresh interval when creating the context. The SDK will periodically re-fetch experiment data in the background.
The unit for the refresh interval varies by SDK. Javascript, Java, Go, PHP, and
Flutter use milliseconds. Python, Ruby, and Swift use seconds. .NET uses
TimeSpan.
- Javascript
- Python3
- React
- Vue2
- Vue3
- Java
- Go
- .NET
- PHP
- Ruby
- Swift
- Flutter/Dart
const context = sdk.createContext(
{ units: { user_id: "user-12345" } },
{ refreshPeriod: 30000 } // 30000 ms = 30 seconds
);
context_config = ContextConfig()
context_config.units = {"user_id": "user-12345"}
context_config.refresh_interval = 30 # 30 seconds (Python uses seconds, not milliseconds)
ctx = sdk.create_context(context_config)
<ABSmartly
sdkOptions={{
endpoint: "https://your-company.absmartly.io/v1",
apiKey: "YOUR-API-KEY",
environment: "production",
application: "website",
}}
contextOptions={{
units: { user_id: "user-12345" },
refreshPeriod: 30000, // 30000 ms = 30 seconds
}}
>
<App />
</ABSmartly>
import absmartly from "@absmartly/vue2-sdk";
Vue.use(absmartly.ABSmartlyVue, {
sdkOptions: {
endpoint: "https://your-company.absmartly.io/v1",
apiKey: "YOUR-API-KEY",
environment: "production",
application: "website",
},
context: {
units: {
user_id: "user-12345",
},
},
contextOptions: {
refreshPeriod: 30000, // 30000 ms = 30 seconds
},
});
import absmartly from "@absmartly/vue3-sdk";
app.use(absmartly.ABSmartlyVue, {
sdkOptions: {
endpoint: "https://your-company.absmartly.io/v1",
apiKey: "YOUR-API-KEY",
environment: "production",
application: "website",
},
context: {
units: {
user_id: "user-12345",
},
},
contextOptions: {
refreshPeriod: 30000, // 30000 ms = 30 seconds
},
});
final ContextConfig contextConfig = ContextConfig.create()
.setUnit("user_id", "user-12345")
.setRefreshInterval(30000); // 30000 ms = 30 seconds
final Context context = sdk.createContext(contextConfig).waitUntilReady();
contextConfig := ContextConfig{
Units_: map[string]string{
"user_id": "user-12345",
},
RefreshInterval_: 30000, // 30000 ms = 30 seconds
}
ctx := sdk.CreateContext(contextConfig)
ctx.WaitUntilReady()
var config = new ContextConfig()
.SetUnit("user_id", "user-12345");
config.RefreshInterval = TimeSpan.FromSeconds(30);
var context = await sdk.CreateContextAsync(config);
$contextConfig = new ContextConfig();
$contextConfig->setUnit('user_id', 'user-12345');
$contextConfig->setRefreshInterval(30000); // 30000 ms = 30 seconds
$context = $sdk->createContext($contextConfig);
context_config = Absmartly.create_context_config
context_config.unit("user_id", "user-12345")
context_config.refresh_interval = 30 # 30 seconds (Ruby uses seconds, not milliseconds)
context = Absmartly.create_context(context_config)
let contextConfig = ContextConfig()
contextConfig.setUnit(unitType: "user_id", uid: "user-12345")
contextConfig.refreshInterval = 30 // 30 seconds (Swift uses seconds, not milliseconds)
let context = sdk.createContext(config: contextConfig)
final ContextConfig contextConfig = ContextConfig.create()
.setUnit("user_id", "user-12345")
.setRefreshInterval(30000); // 30000 ms = 30 seconds
final Context? context = sdk.createContext(contextConfig);
Manual refresh: Call the refresh() method when you know experiment data might have
changed. For example, after deploying a new experiment, or on a periodic schedule you
control.
- Javascript
- Python3
- React
- Vue2
- Vue3
- Java
- Go
- .NET
- PHP
- Ruby
- Swift
- Flutter/Dart
await context.refresh();
ctx.refresh()
const { context } = useABSmartly();
await context.refresh();
await this.$absmartly.refresh();
await this.$absmartly.refresh();
context.refresh();
ctx.Refresh()
context.Refresh();
// or asynchronously:
await context.RefreshAsync();
$context->refresh();
context.refresh
context.refresh()
context.refresh();
For most server-side apps where contexts are short-lived (one per request), you don't need refreshing at all. It's mainly useful for long-lived client-side contexts.
HTTP request options (Javascript SDK)
Per-request timeout
You can override the global timeout for individual HTTP requests. This is useful when a specific request (like context creation) can tolerate a different timeout than the SDK default.
const context = sdk.createContext(
request,
{
refreshInterval: 5 * 60 * 1000,
},
{
timeout: 1500,
}
);
Request cancellation
You can cancel an in-flight HTTP request using an AbortSignal. This is useful when
the user navigates away before the context is ready.
The SDK bundles its own AbortController polyfill for older platforms that don't
support it natively (via absmartly.AbortController). On modern browsers and
Node.js 15+, the native implementation is used automatically.
const controller = new absmartly.AbortController();
const context = sdk.createContext(
request,
{
refreshInterval: 5 * 60 * 1000,
},
{
signal: controller.signal,
}
);
// Abort the request if not ready after 1500ms
const timeoutId = setTimeout(() => controller.abort(), 1500);
await context.ready();
clearTimeout(timeoutId);