Edit: note that there is a "wrong" way to do this as well. The Java thread library provides a stop() function. But since that's exogenous, it doesn't necessarily get cleaned up properly. We had to have an effort to purge it from our codebase after discovering that stopping a thread while GRPC was in progress broke all future GRPC calls from all threads, presumably due to some shared data structure being left inconsistent. "Cooperative" (as opposed to preemptive) cancel is much cleaner.
I understand why they did it - a promise essentially is just some code, and a callback that will be triggered by someone at some point in time - you obviously get no quality of service promises on what happens if you cancel a promise, unless you as a dev take care to offer some.
It's also obvious that some operations are not necessarily designed to be cancellable - imagine a 'delete user' request - you cancelled it, now do you still have a user? Maybe, maybe you have some cruft lying around.
But still, other than the obvious wrong solution - C# had a Thread.Abort() similar to the stop() function that you mentioned, that was basically excommunicated from .NET more then a decade ago, I'm still not happy with the right one.
It's hard to miss all the warnings unless you're literally just looking at the method name and nothing else.
Which what Thread.interrupt does.
You can even link cancellation tokens together and have different cancellation "roots".
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
The catch
You're relying on garbage collection, which is nondeterministic. You don't get to know when the suspended function is collected. For our use case, that's fine. We only need to know that it will be collected, and modern engines are reliable about that.
The real footgun is reference chains. If anything holds a reference to the hanging promise or the suspended function's closure, the garbage collector can't touch it. The pattern only works when you intentionally sever all references.The control flow stops because statements after `await new Promise(() => {});` will never run.
GC is only relied upon to not create a memory leak, but you could argue it's the same for all other objects.
I'm getting so tired of hearing this. I loved the article and it's interesting stuff, but how many more decades until people accept generators as a primitive??
used to hear the same thing about trailing commas, destructuring, classes (instead of iife), and so many more. yet. generators still haven't crossed over the magic barrier for some reason.
I don't really see generators ever crossing into mainstream usage in the same way as the other features you've compared them to. Most times... you just don't need them. The other language tools solve the problem in a more widely accessible manner.
In the (very limited & niche) subset of spots you do actually need a generator, they're nice to have, but it's mostly a "library author" tool, and even in that scope it's usage just isn't warranted all that often.
The first being `.next()` on the returned iterators. If you pass an argument to it, the behavior is funky. The first time it runs, it actually doesn't capture the argument, and then you can capture the argument by assigning `yield` to a variable, and do whatever, but its really clunky from an ergonomic perspective. Which means using it to control side effects is clunky.
The second one how it is not a first class alternative to Promise. Async Generators are not the most ergonomic thing in the world to deal with, as you have the issues above plus you have to await everything. Which I understand why, but because generators can't be used in stead of Promises, you get these clunky use cases for using them.
They're really only useful as a result, for creating custom iterator patterns or for a form of 'infinite stream' returns. Beyond that, they're just not all that great, and it often takes combining a couple generators to really get anything useful out of them.
Thats been my experience, and I've tried to adopt generators extensively a few times in some libraries, where I felt the pattern would have been a good fit but it simply didn't turn out most of the time.
You're right, mostly pointless syntax (along with Promise) now that we can await an async function anyway, especially now with for .. of to work with Array methods like .map
But there are still some use cases for it, like with Promise. Like for example, making custom iterators/procedures or a custom delay function (sync) where you want to block execution.