Skip to content

Implement AsyncContext via TC39 Standard for Async Groups #5673

@ankur22

Description

@ankur22

Feature Description

The current group() API in k6 works well for synchronous JavaScript code but breaks down with asynchronous code. The core issue is that group() cannot maintain the group tag context across async boundaries (promises, callbacks, event listeners).

This causes several problems:

  • Metrics in .then() callbacks lose group context
  • Promise chains all lose group context
  • WebSocket and other event listeners fire after groups complete, causing operations to execute without proper context
  • The group_duration metric is inaccurate because group() returns before async operations complete

This is particularly problematic for users working with k6 browser, WebSocket APIs, or any async patterns, as they cannot organize test results into meaningful sections like "login flow" or "checkout process".

Suggested Solution (optional)

Implement support for the TC39 Async Context proposal using AsyncContextTracker from sobek (k6's JavaScript runtime). This provides a standard mechanism to maintain context across async boundaries.

This approach:

  • Solves all identified edge cases (promise .then() callbacks, promise chains, async/await patterns, callback APIs, goroutine race conditions, duration measurement)
  • Aligns with JavaScript standards (TC39 proposal at stage 2)
  • Provides the best developer experience - async code "just works" without special syntax
  • Has the lowest long-term maintenance burden (no per-API changes needed)
  • Enables future capabilities beyond just groups (distributed tracing context, user context, etc.)

Example of the expected behavior after implementation:

import { group } from "k6";
import { Counter } from "k6/metrics";

let myCounter = new Counter("my_counter");

export default async () => {
    await group("coolgroup", async () => {
        myCounter.add(1); // ✅ Tagged with group=coolgroup
        
        await delay(1);
        myCounter.add(1); // ✅ Still tagged with group=coolgroup
        
        delay(1).then(() => {
            myCounter.add(1); // ✅ Still tagged with group=coolgroup
        });
    });
};

Already existing or connected issues / PRs (optional)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions