In k6, what unit does sleep() from the k6 module take, and what exactly does it block?
answer
- not milliseconds
- float argument accepted
- one VU only, others run on
- blocks the VU's event loop
basics
~10 sk6's sleep() takes a number of seconds, not milliseconds, and accepts fractions, so sleep(0.25) waits 250 ms. It suspends only the calling virtual user, and it blocks that VU's event loop while it waits.
solid answer
~40 s`sleep()` is exported by the `k6` module (`import { sleep } from 'k6';`) and its argument is a number of **seconds**, as a float -- `sleep(0.5)` waits half a second, and `sleep(1000)` waits over sixteen minutes, which is the classic millisecond slip. It suspends only the virtual user that called it; every other VU keeps running. The wait happens inside the iteration, so the function has not returned and the next pass has not started. Because it blocks the VU's execution stack, the event loop cannot run: promises do not settle and `setTimeout()` callbacks do not fire while it waits, which is why k6 points you at `k6/timers` for anything asynchronous.
code
javascript · 9 linesimport { sleep } from 'k6';
export const options = { vus: 1, iterations: 1 };
export default function () {
const start = Date.now();
sleep(0.25); // the argument is seconds, so this is 250 ms
console.log(`waited about ${Date.now() - start} ms`);
}go deeper
Remember the unit: seconds, and fractions are allowed. If you find yourself typing a number in the hundreds, you have probably reached for milliseconds out of habit.
Explain that the call suspends only the calling virtual user and that the wait lives inside the iteration, so it shows up in per-iteration timings rather than between them.
Be able to diagnose the async trap: a script that mixes sleep() with promise-based code stalls because the stack never empties, and the fix is the k6/timers module, not a shorter sleep.
Decide as a team where blocking waits are acceptable at all. Once any part of a suite uses async APIs, a blocking sleep in shared helper code becomes a hazard everyone inherits.
## What `sleep()` is `sleep()` is one of the five things the `k6` module exports (alongside `check`, `fail`, `group` and `randomSeed`). You bring it in with `import { sleep } from 'k6';` and call it inside the VU function to suspend that virtual user for a while. It is a plain blocking call. There is no callback, no promise, nothing to `await`: control simply does not come back until the wait is over. k6 converts the number you pass straight into a wait duration, with no clamping and no minimum. `sleep(0)` returns essentially at once, and a negative value produces a wait of zero length rather than an error. There is no upper bound either: a large number really does hold that virtual user for that many seconds. ## Seconds, not milliseconds This is the single most common mistake with it. The parameter is a number of **seconds**, and it is a floating-point number, so fractions work exactly as you would expect. | Call | How long the VU waits | |---|---| | `sleep(1)` | one second | | `sleep(0.25)` | 250 milliseconds | | `sleep(Math.random() * 2)` | a random span between 0 and 2 seconds | | `sleep(1000)` | about 16 minutes 40 seconds -- the millisecond mistake | Every timing API most JavaScript developers reach for first -- `setTimeout`, `setInterval`, `Date.now()` arithmetic -- is denominated in milliseconds, so `sleep(1000)` looks right and is wrong by a factor of a thousand. In a short run it usually shows up as a test that produced almost no traffic. k6 does not try to guess which unit you meant. A number is a number of seconds, and nothing warns you when the resulting wait is implausibly long for the run you configured. ## It suspends one VU, not the test While a virtual user is inside `sleep()`: - **that VU does nothing else** -- no requests are in flight for it, and its function is frozen at that line; - **every other VU carries on unaffected** -- each virtual user runs its own function independently, so one sleeping user does not hold up the rest; - **the wait is inside the iteration** -- the pass is not over until the function returns, so the seconds spent sleeping are part of that iteration's own elapsed time, not a gap between iterations. That last point catches people out when they compare a run's iteration timings against its request timings and find a gap they cannot account for. The gap is the sleeping. ## It blocks the event loop `sleep()` blocks the VU's JavaScript execution stack, and the ECMAScript specification only lets the event loop run callbacks once that stack is empty. The practical consequences are sharp: 1. A promise that would have settled during the wait **cannot settle** -- its continuation is queued behind a stack that never empties. 2. A timer callback scheduled with `setTimeout()` **cannot fire** during the wait, however short its delay. 3. Polling for a condition with `while (!done) sleep(1)` therefore **never terminates**, because nothing that could set `done` is allowed to run. For a wait that has to coexist with asynchronous work, `setTimeout()` from the stable `k6/timers` module is the non-blocking alternative -- it schedules a callback instead of freezing the stack. ## Where it sits in a two-step iteration In a browse-then-add-to-cart iteration, `sleep()` normally appears between the two steps and again at the end: ```javascript import http from 'k6/http'; import { sleep } from 'k6'; export default function () { http.get('https://quickpizza.grafana.com/'); sleep(1); // seconds http.post('https://quickpizza.grafana.com/api/pizza', body, { headers }); sleep(1); } ``` Both waits are inside one iteration. The function has not returned, so k6 has not started the next pass. ## When the test is stopping `sleep()` is tied to the virtual user's run context. If the test is being torn down while a VU is mid-sleep, k6 stops the timer and the call returns immediately rather than running out its remaining seconds. A script full of long sleeps therefore does not add its sleep durations to how long the run takes to shut down. ## Getting it right - Use seconds; if you are thinking in milliseconds, divide by 1000 before you type the number. - Do not mix `sleep()` into `async` code -- reach for `k6/timers` there instead. - Never use it to poll for a condition, because nothing that could change the condition is allowed to run while the stack is blocked. - Remember the wait belongs to the iteration, so it is visible in per-iteration timings rather than as a gap between them.
- Why does k6 warn against using `sleep()` with async code?`sleep()` blocks the VU's execution stack, and the event loop only runs callbacks when that stack is empty. So a promise that would settle during the wait cannot settle, and no `setTimeout()` callback can fire. For a non-blocking wait, use `setTimeout()` from the `k6/timers` module instead.
- What happens to a sleeping k6 VU when the test is being stopped?The sleep is bound to the VU's run context. Once that context ends, k6 stops the timer and `sleep()` returns straight away rather than serving out its remaining seconds, so long sleeps do not lengthen how long the run takes to shut down.
saying these in an interview costs you the question
- Writes sleep(1000) expecting a one-second pause
- Thinks sleep() pauses the whole test rather than one VU
- Expects promises to settle while sleep() is waiting
- Believes the sleep happens between iterations, not inside one
- Polls a condition with sleep() in a loop and wonders why it hangs