A long time has passed, but still: the solution may be http://jscex.info/
Javascript is single-threaded in nature, and it is a design choice because multithreading is a difficult topic. 99% of random javascript developers will not handle properly.
Workers are the only way to get a different thread, rather than blocking the user interface, but to make them usable without the dangerous side effects of real multithreading, they work in a completely separate context, as you noticed. Thus, they are more like calling an external command that passes command line parameters than creating another thread.
Thus, working in "async" mode is the only solution right now, but since you did not wait for a button to be pressed or a remote connection, the only async event you can associate with is a tick timer, which leads to poor code style, which causes long operations in js.
However, there is a small library that I think is very interesting and completely unknown that (despite this a bad site) is able to "convert" on the fly a beautifully written procedural code into a mess of timers and functions, an asynchronous model requires: http://jscex.info/
As in Windows 3.1, you just need to “give in” ($ await (Jscex.Async.sleep (50));) for a while in the browser so that it does not completely freeze. It will really freeze under the hood, but if you get it often enough that no one will ever notice :) (after all, so multitasking still works inside each core of your processor, very small fragments of time during which the processor is 100% working over one set of instructions .. take this up to 20 ms, no one can tell).
I think this can help you “create” a coroutine similar to a coroutine, without actually “writing” such code, but delegating to the “precompiler” the launch work.