Async Background Processing in the Browser
There's a piece of code in our UI that splits a freeform SQL string into separate queries, which are then stored on the backend. We have no…
There's a piece of code in our UI that splits a freeform SQL string into separate queries, which are then stored on the backend. We have no control of the users input, the SQL string varies in length, complexity and all sorts of quirks.
The core of the query splitting is a Regular Expression I have found on Stack Exchange. It was originally designed for PHP, so I had to modify it. It is not the best solution, but at the time of putting this code together I could not find anything better. I started thinking about a streaming SQL parser with a finite-state machine just to split SQL string into separate queries, but that Regex seemed to do the job…
Couple of weeks ago one of our clients complained that editing his query freezes his browser. I have narrowed it down to this string:------THIS KILLS IT----
The Regex looped infinitely and did not spit out any result. The UI froze. I simulated the whole thing in https://regex101.com/, where it spat out an error.
Hey, so the Regex is not working 100 % correctly. Hey, the site is able to catch the failing Regex and stop the execution! Stopping execution will do it for me. The query is invalid, so I'm not worried about not parsing it at the moment and not letting the user save it. I “reverse-engineered” the regex101.com's code (pretty printing and breakpoints in Chrome Inspector are powerful tools) and figured out that it's using Web Workers to run the Regex in the background. After a certain delay (2 seconds) the worker status is checked and if it's still processing, it can be terminated.
The body of the worker and its wrapper is very simple.
The Web Workers API defines a
message event that you attach your listener to and then a
postMessage function to which you pass the result. The wrapper promisifies the worker's execution. The most important part here is the
setTimeout function that checks the worker's result after 2 seconds, and if the worker does not finish the execution, the worker will be terminated and the promise rejected.
By using Web Workers I'm able to prevent freezing the UI when parsing a user input using a Regex. I added a debouncer on top of that to trigger the workers sparsely, rather than on every keypress.