Hacker News new | ask | show | jobs
by dumbo-octopus 702 days ago
One could theoretically pull out the shared boilerplate to a utility function like:

    func runTask[T any](task func() (T, error)) chan result[T] {
        ch := make(chan result[T])
        go func() {
            defer close(ch)
            res, err := task()
            ch <- result[T]{ el:res, err:err } }()
        return ch }
Does that sort of thing happen much in practice?
2 comments

Yes, it does, and Go is perfectly capable of it, and many libraries exist for you to choose which exact method suits your problem and temperment.

One of the common pasttimes in the threaded versus async debate is to present code in which one side uses all sorts of helpers and patterns and libraries and the other side is presented through writing it "raw". The great-grandparent of my post here is guilty of this. While there are interesting reasons to debate threaded versus async code, this is not one of them. Both of them are absolutely capable of writing the moral equivalent of "output = parallel_map(myMapFunc, input)" and all similar operations to within practical epsilon of each other, and anyone citing this sort of thing as an argument on either side should probably be ignored. And both languages will feature code written by people who don't know that, and it shouldn't count against either.

No… I fear you’ve missed the entire point of the matter, which is that async/await requires that you must go all the way up the call stack explicitly “await”ing things when you have introduced an “async” call (or similar wide spread changes to that effect). There’s no special magic utility function you can call you hide it away. That’s the whole point – and a very good thing, this thread argues.
No, it is perfectly feasible to abstract around it. It's just that the abstractions are also colored. But there is no more a rule that you can only "await" a promise right in the exact code where you created the promise than there is that the only way to use threads is to spawn them right on the spot and then wait for the result right on the spot. Critics of both async and threads are just dead wrong on this, and observably, objectively so, since libraries in both cases not only exist, but are readily available and abundant.

And I'm admitting this "against interest", as the lawyers say. I'm not striking a disinterested "middle of the road" pose here. I'm hugely on the side of threads. But it is still not a relevant criticism of async. You can easily "parallel map" with either abstraction and you are not stuck unable to abstract the control flow in either case.

Show me a JS library that allows you to swap a non-async call for an async one in a non-async context^ and I’ll eat my hat.

^Without any non-local changes, obviously.

You can, but I didn't want to introduce any extra constructs from the original example.

And this is a bit of a weird case, at least where I am. I tend to have a bunch of things to process and have one goroutine sending keys/indices/etc to a channel that multiple workers are processing off of.

We did have an abstraction for that at one point, but there were enough edge cases in our domain that we either had to develop a config system or rip it out and go back to writing each one (we went with the latter after an attempt at the former went really bad).