6 ms·
Go Concurrency vs. RxJS
- deleted 2y ago[deleted]
- miiiiiike 2y agoLearning RxJS made me a better programmer faster than anything since the Gang of Four Design Patterns book 20 years ago.
- roblh 2y agoWhat did you use it for, specifically? A personal project, or did you work on something that already used it heavily? I’ve played with it, and it’s interesting, but I haven’t quite found a reason to use it yet.
- miiiiiike 2y agoI use it in several places in my company. Being able to compose multiple async streams of data is the best part. One simple example is a scroll tracker. I needed to track a user’s scroll, determine the direction, and combine it with touch events. I wrote a prototype using event handlers and the code descended into incomprehensible spaghetti pretty quickly. With RxJS I was able to implement it as a series of custom operators that were easy to write, test, and use.
- sollniss 2y agoThe font colors make this unreadable.
- xyst 2y agoSwitching to reader mode makes the text readable but code samples a mess. Can’t win
- anonzzzies 2y agoI had the same; you probably have your phone in darkmode.
- steve_adams_86 2y agoIf you’re in light mode it helps a bit.
- kopirgan 2y agoThought it was my phone..
- jchw 2y agoConcurrency in Go is overrated; channels are error-prone and not as flexible as they probably could/should be. Though, I honestly find moderately complex Go code far easier to maintain than moderately complex RxJS code, having had both in my career at different times. That said I will admit that sometimes having the RxJS toolkit at your disposal makes solving some problems very easy. It's just that when I construct an RxJS pipeline that does some complex stuff end-to-end, don't expect me to be able to readily explain it a couple months later. It's not write-only... But it gets thick easily. One issue I think people underrate that impacts both promises and RxJS is error handling. Exceptions in JS can be a lot of different things, including programming errors like TypeError; but most people will write error handling that doesn't actually distinguish the two cases. In fact it's hard to blame them as this can make for a lot of error-prone boilerplate to deal with, but it's especially annoying as it can hide programming errors in plain sight, making them hard to spot for your observability tooling and harder to debug (it's frequently not a possibility people give much thought to.) Then again, on the Go end, I find it hard to try to handle async errors as well, though for totally different reasons. There's lots of good ideas floating around with Go and I even kind of like contexts to a degree, but it feels like there's no cohesive story that binds it all together. I used to use tomb, but I found myself wanting even more than it offered.
- phplovesong 2y agoI argue CSP is far superior to async/await in almost all cases. In Go errors are explicit and always right there. No weird control flow like you see in javascript.
- phplovesong 2y agoHow can these even be compared? Go does concurrency on multiple CPU cores, and javascript one one. Go can so both IO and CPU bound tasks with the SAME way, and in javascript land you really dont do any CPU tasks async at all (makes no sense to do so).
- fhfhfhjfnfnfmf 2y ago90% of Go code isn’t CPU bound so that’s irrelevant.
- lloydatkinson 2y ago????
- kermerlerper 2y agoThe async implementation is not shown in the article, but could easily do a number of things such as - http request with decoder information - worker threads
- cyberax 2y agoJS concurrency is crap. It should be shot and buried in a lead coffin. Debugging async code is pure hell. With Go, you have a normal debugger that can be used to step over the code. You can get normal stack traces for all threads if needed. There is a race detector that can catch most of unsynchronized object access. With JS? You're on your fucking own. You can't find out the overall state of the system ("the list of all threads"), without getting deep into the guts of React or whatever framework you're using. Debugger is useless, as each `await` call drops you into the event loop. So pretty much every complicated non-trivial JS app ends up with _tons_ of race conditions, by depending on the order of async functions finishing. Speaking of race conditions. Coming from classic multithreading and Go, I tried to use `Promise.race` to simulate the `select` statement. Turns out that in JS it is actually useless because it LEAKS MEMORY BY DESIGN: https://github.com/nodejs/node/issues/17469 https://github.com/nodejs/node/issues/17469 Back to the article. It uses pre-generics Go. In more modern Go, you can use something like Conc to reduce the boilerplate: https://github.com/sourcegraph/conc?tab=readme-ov-file https://github.com/sourcegraph/conc?tab=readme-ov-file
- arctek 2y agoIt's also unintuitive in the sense that if you are racing you usually want to discard/stop the other Promises that didn't finish in time. But the only real way to do this is to pass through an AbortController down into whatever networking/IO task (and your own code for that matter) is happening to "cancel" it that way. Otherwise you just end up with the result of the fastest Promise and the rest will continue when they get a chance.
- aporetics 2y agoI thought the point of using observables over promises is that they have cancellation, and presumably an implementation of race would cancel any slower request (i.e., handle the abort controller automatically)? Is that not the case?
- arctek 2y agoMy comment was just on the standard behavior. Not sure when it comes to observables, probably comes down to how you are implementing it. But in general it's impossible to cancel a hanging Promise, you have to have some kind of signalling mechanism that either throws so it rejects or otherwise resolves the Promise chain.
- paulddraper 2y agoRxJS is useful and overused whenever I've seen it.
- arunc 2y agoWe ditched both and rewrote our services in Kotlin. We never looked back. Coroutines FTW!
- s6af7ygt 2y agoCan you elaborate a bit more, so us ignorant people may share in your excitement? I've seen many people switch from JVM to Go, but I've not seen a lot of people switch from Go to JVM.
- lenkite 2y agoCompare the runtimes also and how easy it is to debug and maintain RxJS vs Go. The former is a nightmare to maintain.
- steve_adams_86 2y agoI always found RxJS fairly awkward. I assumed it was a skill issue (and I still think it is), but gradually learned it for personal and professional projects. It's fine. I avoid it now. I think Go is orders of magnitude better in terms of general concurrency tooling, though I know some people straight up hate it. I've recently started using Effect, the TypeScript library, and I find it's so much better than RxJS as well. Now that I've gotten the swing of things, I can't imagine going back to RxJS. Although I prefer Go in many ways still, there are things about Effect I actually prefer over tooling in Go as well. And of course, there is no direct comparison (one is a library in a language that's a superset of another language, the other is just a language), but when it comes to solving the problem of concurrency, Effect is great. I recommend it. Effect is also a lot more than RxJS. It has schema and data tools, primitives for error handling and control flow, state management, etc. Regardless, where they overlap, I think Effect is much better designed. Where Effect (and RxJS) falls apart for me is debugging. I really hope that story improves. The errors in TypeScript are pretty brutal, and actually tracing through calls isn't as intuitive as it appears it will be at times. Weird stuff goes on. Things get convoluted quickly. Somehow that disadvantage hasn't prevented it from becoming a go-to tool for me, so far.
- whazor 2y agoMy biggest issue with RxJS is that it is so easy, that you can easily make pretty bad bugs. In particular, you can have a `mergeMap` or a `switchMap`, and with RxJS they are very similar in API. But if you pick the wrong one, it can lead to weird bugs in your application. However, if you would manually program promises and maybe debounce helpers, you would be more likely to program the correct behavior. Still, the easiness RxJS is pretty cool, and if you are an expert at it, you could build complicated applications with very little code. Also Marble Testing is a cool way of testing certain application behaviors. Overall, I would recommend searching decent alternatives. Since RxJS is too hard to get right.
- steve_adams_86 2y agoHaha, after all this time I still don’t find RxJS easy unless I’m using it for a common, familiar pattern. I agree otherwise, though. It’s definitely easy to create weird bugs.
- raulns86 2y agoOne strong point about RxJS is marble testing, I haven't found something similar for async systems with complex scenarios.
- sunnyque 2y agoguy is using unbuffered channel and wonder why it working slow, lol
- tankenmate 2y agoyeah, that was my first thought as well; I suspect it is a lack of depth in Go experience.
- reactor 2y agoMeanwhile, Java just upped the game of virtual thread with https://mail.openjdk.org/pipermail/jdk-dev/2024-October/009429.html https://mail.openjdk.org/pipermail/jdk-dev/2024-October/0094...
- mdhb 2y agoAnd Dart is just starting next week to implement this which should start putting it into an different category of language entirely. https://github.com/dart-lang/language/blob/main/working/333%20-%20shared%20memory%20multithreading/proposal.md https://github.com/dart-lang/language/blob/main/working/333%...
- deleted 2y ago[deleted]
- deleted 2y ago[deleted]
- Alifatisk 2y agoWhy was Dart mentioned under the broadcast headline? I didn't quite get it.
- kermerlerper 2y agoIt's a joke.
- deleted 2y ago[deleted]
- capn_duck 2y agoThe JS examples don't seem too demonstrative to me. Especially for someone not very familiar with RxJS. Any time you're wrapping something with `from` or `of`, I raise an eyebrow export function pipeline(in$: Observable<Product>): Observable<string> { return in$.pipe( mergeMap(product => from(product.Images)), ); } Why use mergeMap at all here? Why not not just return in$.pipe( map(product => product.Images), ); I get that this is a toy example, not trying to be pedantic.
- kermerlerper 2y agoThe former pipeline takes in products, splits the images, and processes each one. The latter pipeline would releases batches/arrays of images.
- EchoStar27 2y agoHaving worked extensively with both Go and RxJS, I’ve come to appreciate the distinct strengths each brings to the table, though I lean towards Go for its clarity in managing concurrency. RxJS, with its powerful operators and composability, makes handling complex asynchronous flows relatively succinct. But that same power can lead to hard-to-trace bugs when things go wrong, especially with the more subtle operators like switchMap and mergeMap. Go, on the other hand, offers a more explicit concurrency model through goroutines and channels, which can be less error-prone in larger systems if used judiciously. The trade-off for Go’s more explicit concurrency is that it can feel more verbose, but the payoff in long-term maintainability and debugging is hard to overlook. RxJS is still a great tool for certain problems, but in terms of reliability and ease of debugging, Go wins out for me in most cases.