I think that even with generators, you don't quite get Go style concurrency due to lack of concurrency for non-IO bound tasks. For a real world use case, suppose you are compiling TypeScript. Without web workers or running in a separate process, the compilation will block because it is CPU bound. You can get around this with generators by explicitly putting in pause points through yielding, but that is assuming that one can break up the task and that it doesn't depend on some external blocking library.
You may possibly be conflating concurrency and parallelism. JS has concurrency due to its event-based programming model, but not parallelism due to its single-threaded nature.
The TypeScript compilation you mention likely has many concurrently running tasks, just not in parallel (modulo the I/O thread pool as you mention).
When you use Generators for this style of concurrency, generators are comparable to async/await. The difference is that with async/await support, you still have the option for your promises to resolve generators.
Basically by using generators for co-routines, you preclude yourself from some use-cases that generators normally provide.
> Generators are not as useful and are more niche. Am I wrong?
I would say that generators are more general, and async/await actually more specific to a single use-case.
I think it is a bad idea to use generators to emulate threads. Because the code becomes difficult to understand and it is easy to make a mistake (for example forget to put a `yield` operator somewhere). I saw examples of such code.
If you want threads, then make a syntax or API for them rather than implement hacks nobody will want to deal with later.