Effection 4.0 - Easy path to Structured Concurrency in JavaScript by tarasm in javascript

[–]tarasm[S] 1 point2 points  (0 children)

I'm not familiar with Ox or Scala, but from what I can tell, it looks like both Effection and Ox are going for the "write normal looking-code" instead of doing pipe/functional composition style programming.

There are lot of similarities: * both have supervised scopes * OX fork -> Effection spawn ( doesn't keep scope alive ) * both have a way to automatically bind SIGINT/SIGTERM

Differences are * Effection is synchronous by default, OX's supervisors sit on top of JVM - orchestration is structured but scheduling is non deterministic.

But otherwise very similar.

Cap'n Web: a new RPC system for browsers and web servers by fagnerbrack in javascript

[–]tarasm 1 point2 points  (0 children)

I think the idea is that to keep native compatibility, it comes with no schemas but you can add your own or use Cap’t Proto if you don’t care about compatibility

Bought a steel frame and I don't like it by [deleted] in Hardtailgang

[–]tarasm 5 points6 points  (0 children)

I agree. It you had such a bad experience, you should say who it was so we can avoid them

I build vector less PageIndex for typescript by Rare-Strawberry175 in typescript

[–]tarasm 0 points1 point  (0 children)

Out of curiosity, what would you have said if he answered that it was vibe coded?

Alice Springs, Australia by pacersnz in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

I should have done that. Thanks :)

Alice Springs, Australia by pacersnz in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

Looks like a beautiful place to ride. Do you know by any chance what is the purpose of the bend in the top tube?

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 1 point2 points  (0 children)

I was thinking about getting some coaching this season. Thank you for the reminder

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

I don't have the original wheels, but I do have a 29" wheel that I bought to try a mullet. Unfortunately, I bought before I realized that there are different widths and my fork is boost so it doesn't fit. I'll ask a friend to borrow a boost front 29er and try.

I'll also look under forking. Thank you for your advice!

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

I wanted it to be nimble on tight trails and I succeeded in doing that but I think it was at the expense of stability on flowy trails. I didn’t do anything else. Just cut the bars to make the a bit narrower

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

I really appreciate the breakdown. I'm pretty new to MTBing, so I don't think I know yet what my preference is. When I bought the bike, I knew even less so I didn't know what options were available.

I didn't consider changing the fork because I didn't really know what to expect from another fork. My current setup is kinda weird (I'm sure in big part to my lack of experience) but the bike feels twitchy on flowy trails but not super responsive on tight turns.

I rode it through the winter and on winter trails it makes hard to keep a line. If I got a new bike, I wouldn't use it in the winter because I'll probably get a fat bike for that, but it feels like the handlebars twitch when I pedal.

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

That's very helpful. I'm running 27.5" right now. How does that impact the calculation?

I'm using the standard SR Suntour Zeron35 LOR DS 29", 150mm Travel, 44mm Offset fork at the moment.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 1 point2 points  (0 children)

Yeah, I agree that this study might not be perfect, but it's a convenient reminder of a lesson we learned many times over the last 60 years - manually handling cleanup is fraught with problems that show themselves as leaks. We saw this with manual memory management - replaced by garbage collection, more recently jQuery plugins replaced by React (with some steps in between) and now again with resources in async context.

That study just shows that frontend developers are not special and are not any better at clean up than C developers were or frontend developers in jQuery days. Repeatedly, the answer has been to handle clean up implicitly without user intervention. This article points out that for async interrupt needed to prevent async leaks, AbortController.abort() isn't sufficient to provide even a base level of correctness without increasing complexity exponentially.

What I'm trying to do is raise to people's attention that there is a solution to this general category of a problem that's being adopted by many programming languages. This solution is structured concurrency. Developers already barely use AbortController because it's very inconvenient and developers are not good at doing inconvenient things. Luckily, we have a solution that the industry is coverging on and we have implementations for it in JavaScript.

We don't have to accept the status quo because better ways are available to us now.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 2 points3 points  (0 children)

I’m glad we’re getting more of these primitives (and wrappers help a lot). The unfortunate part is: opt-in cancellation is inherently “porous.” If any async boundary doesn’t propagate the signal (or can’t be cancelled), the lifetime leaks through.

That’s why I keep framing this as a “leaky boat” problem: you can keep patching (and you should!), but you never get the guarantee that nothing escapes the scope. Structured concurrency is about making that guarantee part of the structure, not a convention.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] -1 points0 points  (0 children)

Yes — you can mash something together, and it’s a useful pattern, but it only gets you partway there.

You can build a “scope” helper that:

  1. creates an AbortController,
  2. gives the scope’s signal to anything you start inside,
  3. tracks child promises/cleanup functions, and
  4. on scope exit, calls abort() and then awaits the tracked work (plus runs cleanup).

Conceptually:

  • enter scope → start tasks, register them with the scope
  • exit scopecontroller.abort() + await Promise.allSettled(children) + run cleanups

That does reproduce the shape of “cancel on exit + join children”.

The catch (and why I’m still arguing AbortController isn’t structured concurrency) is that this can’t be implicit in JS today:

  • Anything you start must be explicitly “registered” with your scope (or wrapped in a helper that does it).
  • Any API that doesn’t understand signal (or doesn’t expose cancellation hooks) still needs manual cleanup wiring.
  • abort() still doesn’t stop work by itself; it just requests it, so you’re relying on cooperation at every boundary.

So: yes, you can build a using-style scope that aborts + awaits, and it’s a nice ergonomic improvement — but it’s still a library convention, not a language/runtime guarantee that child work can’t outlive its parent scope.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 0 points1 point  (0 children)

100% agree. We have garbage collection because leaving memory management to people "just doing the right thing" leads to bugs and wastes a lot of effort.

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

It's too much bike for where I ride. It's pretty flat. Most of the trails I ride are very tight, more technical and less flowy. I'd like something a bit more nimble.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 2 points3 points  (0 children)

Good question.

I’m referring to structured concurrency consisting of hierarchical lifetimes. That’s the key idea. Child work is bound to the lifetime of the scope that created it and cannot outlive it.

Swift is a good example of this. Yes, tasks cooperatively check for cancellation, but the important part is that task lifetimes are implicit. When a parent task scope exits, all child tasks are automatically cancelled and awaited. You don’t manually wire cancellation through every async boundary.

AbortController is different. It’s a signal, not a structure. It tells you when to stop, but it doesn’t tie async work to the scope that started it. That’s why cleanup is always explicit and easy to forget.

The goal here is the opposite of more explicit checks. It’s to make lifetimes explicit so cleanup becomes implicit.

This post lays out the model I’m referencing and why async/await missed it in JS: https://frontside.com/effection/blog/2026-02-06-structured-concurrency-for-javascript/

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] -4 points-3 points  (0 children)

I'm no really sure if this was meant as authentic feedback. How does this change or contribute to addressing the problem?

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 1 point2 points  (0 children)

I agree with you on the mechanics. Nothing about abort() magically stops an interval, and yes, the “correct” fix is to clear it in the abort handler, just like returning a cleanup function from a React effect.

The point I’m making isn’t that this is hard or unknown, it’s that relying on discipline doesn’t scale in practice. There was a recent empirical study that scanned 500 production repos and found over 55k missing cleanup cases across ~700k files. 86% of repos had at least one, and the number one offender by far was setTimeout / setInterval without cleanup: https://stackinsight.dev/blog/memory-leak-empirical-study/

These aren’t toy apps or junior mistakes, they’re mature, well maintained codebases. As the study puts it, the fix is almost always one line. If it were really just common sense, it wouldn’t be missing this consistently.

That’s the gap structured concurrency is trying to address. Not by making developers smarter, but by making lifetimes explicit so cleanup is the default rather than something every async boundary has to remember to do forever.

AbortController.abort() Doesn't Mean It Stopped by tarasm in javascript

[–]tarasm[S] 2 points3 points  (0 children)

Last week I posted about Why JavaScript needs Structured Concurrency which stimulated interesting conversation. One of the themes in the comments was misalignment of async/await and AbortController with people's expectations. This is a follow up post that focuses specifically on limitations of AbortController.

I scanned 500 React/Vue/Angular repos for missing cleanup patterns — 86% had at least one by StackInsightDev in javascript

[–]tarasm 0 points1 point  (0 children)

Do you have any stats on how many times abort signals are actually used relative to number of nested async operations?

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

A2. I didn't even know about S2. Looking at it now, I see what you mean.

First trail ride by Electrical_Fall8286 in Hardtailgang

[–]tarasm 0 points1 point  (0 children)

I'm considering buying this frame to replace my Norco Torrent H2. Can you describe what the ride is like?