The Independent Blueprint for Modern Load Testing & Grafana k6 Performance Engineering

The k6 Node.js Event Loop Paradox and Webpack Bundling

Posted on May 24, 2026 in Architecture

If you write a lot of JavaScript, you are probably familiar with how Node.js and modern browsers handle asynchronous execution. You write `setTimeout`, you create `Promises`, and you use `async/await` to handle background operations. You expect things to happen out of order, managed by a slick system-level event loop. This expectation works perfectly in web applications, but it creates a massive footgun when you try to apply it to Grafana k6.

This is what I call the Node.js event loop paradox. The Sobek JavaScript engine embedded within k6 compiles and executes your code procedurally within the lifecycle of each iteration loop. It does not possess a native Node.js event loop. Let us look at why this matters and how to handle external libraries correctly.

Why There is No Event Loop

The core philosophy of a load testing virtual user is strict control over execution pacing. Under a standard test model, k6 needs to know exactly when an iteration starts and when it finishes. If a VU could spawn asynchronous background tasks that executed out of order, the engine would lose the ability to track metrics accurately. Response times would become muddled, and coordination between VUs would break down.

Because Sobek does not feature an active event loop, standard asynchronous JavaScript APIs will fail. If you try to use `setTimeout` or `setInterval` in a k6 script, it will throw an error. Promises that rely on process-level microtask queues will fail to resolve, resulting in broken execution or stalled tests. Your JavaScript code executes top-down, sequentially, blocking execution until each statement completes.

The Packaging and Bundling Problem

This lack of an event loop creates a second problem: you cannot import raw npm packages directly into k6. Many npm libraries rely heavily on Node.js core APIs (such as `fs`, `path`, or `process`) or assume the presence of a global browser window. Because Sobek is a sandboxed, Go-native environment, these APIs simply do not exist.

If you want to use an external utility library (like Lodash or a custom hashing module), you cannot just call `npm install` and run your script. Every external library must be pre-processed, bundled, and transpiled into a self-contained module before k6 can evaluate it. This requires using asset bundlers like Webpack, Babel, or Rollup to compile modern JavaScript into a flat, ECMAScript 5 or 6 compatible file.

How to Bundle Assets for k6

To compile raw npm modules for k6, you need a custom Webpack configuration. Here is a production-ready Webpack setup that packages modern JavaScript libraries into self-contained ESM files that Sobek can evaluate easily:

const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/entrypoint.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    libraryTarget: 'commonjs',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
  target: 'web',
  externals: /^(k6|https?\:\/\/.+)/,
};

This configuration transpiles modern syntax into highly compatible ECMAScript, while marking k6 core modules (like `k6/http` or `k6/metrics`) as external imports. This ensures that your bundler does not try to package internal Go bindings into the JavaScript output.

By understanding the limits of the Sobek virtual machine, you can write highly optimized scripts that compile quickly and execute without memory overhead. Stop treating k6 like a Node.js process: it is a Go concurrency engine running a sequential JavaScript interpreter.

Now that you understand the parser limits, check out how workload profiles behave under load in Coordinated Omission: Why Open-Loop Workloads Are Vital for Real Load. To write enterprise-grade scripts that optimize local VM memory, read Enterprise k6 Scripting: Custom Telemetry, Thresholds, and SharedArray. If you are developing these scripts on a Windows terminal, see our operational guide on Windows Environment Variables: Handling State in PowerShell and CMD for k6.