Skip to content
Luminesca.
Journal · Tool reliability

When a regular expression freezes the page: a cancellable browser tester

A regular expression that looks short can take much longer to finish than a long one. Our tester originally ran matching on the page itself. We moved that work into a disposable Worker so an expensive pattern can be stopped while the controls remain responsive.

A small input that exposes the problem

In the Regex Tester, enter (a+)+$ as the pattern. For text, use 36 copies of a followed by an exclamation mark. Leave the global flag enabled. The final non-matching character forces the engine to work through alternatives. The exact running time depends on the browser and its regular-expression engine.

Our regression uses this input in a separate thread and checks that the work can be terminated. Browser validation also checks that the page remains responsive during a costly match. Do not paste this example into an unrelated production system to test its limits. Use the bounded tester, and start with a shorter input if you only want to inspect matching behavior.

Why a Worker helps

A synchronous matching loop occupies the same thread that responds to the page's buttons. Putting a timer next to that loop does not make it interruptible: the timer's callback still needs a chance to run. Our replacement sends the pattern, flags and text to a dedicated Web Worker and leaves the page to manage controls and status.

The page can terminate that Worker when the user cancels or a timeout fires. This separates the expensive computation from the interface; it does not make the expression itself efficient. MDN's guide to Web Workers explains the separate execution context and message-based communication.

The limits are visible parts of the tool

  • The page requests termination after two seconds of matching work, including Worker startup.
  • Text input is limited to 100,000 characters and the pattern to 2,000.
  • At most 1,000 matches are displayed and copied.
  • Capture previews show at most 20 groups and 200 characters per group.

The timeout is scheduled by the browser, so it is not an exact real-time deadline. Background-tab throttling and device load can delay callbacks. We show a stopped message when that callback runs, instead of presenting a partial result as a successful full scan.

If the match limit is reached, the result says that only the first 1,000 matches are shown. Copy uses those matches, not a hidden complete result. Capture-preview truncation does not shorten the full match value used by Copy.

Editing the input invalidates the old work

A long-running request might otherwise finish after the user has entered a different pattern. The tester now cancels queued or active work when an input or flag changes, clears old results and gives the next request a new revision number. Only a response for the current revision can update the page.

That rule also applies to Copy: it stays disabled until the current request has useful results. Cancellation leaves the inputs available for editing. When browser policy prevents a Worker from starting, the page displays a start error; it does not silently fall back to a synchronous match that could freeze the interface.

Zero-length matches and Unicode need their own checks

A pattern can match an empty position without consuming a character. Repeating the same search from that position would never advance. The worker explicitly advances after such a result. With Unicode matching enabled, it advances over the full Unicode code point rather than stopping inside a surrogate pair.

Our regression expects two empty matches for (?:) against a single emoji with the global and Unicode flags enabled: before and after it. Other fixtures cover ordinary captures, invalid expressions, whitespace as a valid pattern, input limits and the result cap. These tests catch different failures from the slow-pattern case.

What to do when your pattern times out

First reduce the text to a small example that should match and another that should fail. If your task is simply to accept a run of the letter a, ^a+$ expresses that without the nested repetition in the demonstration. That replacement is specific to this example; blindly rewriting another pattern can change its meaning.

Then add realistic cases gradually and check the flags. This tool uses JavaScript regular expressions, so a pattern accepted here may behave differently in a database, another programming language or a different engine. Match counts alone do not establish that an expression is correct for your application.

For a bug report, send the pattern, flags, a short non-sensitive input and the observed status through our contact page. This case study records the September 22, 2026 implementation and verified fixtures. It is not a general security guarantee for all regular expressions or browsers.