Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

the problem is that async-ification infects everything up the call chain. converting a synchronous loop to async may now require that you change a lot of your sync code above that loop into async code as well, which can complicate all sorts of stuff like debugging, profiling, error handling.

for this reason i always optimize the shit out of all synchronous code before resorting to async. and if you do need to go async after that, you might be better off just offloading that optimized synchronous op to a worker thread (or to wasm) to not block the UI.

modern JS vms are insanely fast when you pay attention to GC pressure, mem allocation, monomorphism, and dont use accidentally quadratic algos. it's rare that i encounter a codebase that can't be sped up by some large multiple without ever resorting to an async/defer crutch.



If this was any other language than JS I would agree but my personal experience with JS is the opposite.

In my experience almost everything in the JS world is already async. User interactions are async, requests are async, almost all NodeJS APIs are async. To me having to add more async in JS is a tiny barrier compared to what I'm facing in other languages that feel more synchronous to me.

Since there is already so much async I also feel like debugging, profiling and error handling are all pleasantly solved problems in JS.

Offloading to workers is also async so while there are many valid benefits to be gained, avoiding async does not seem like one of them to me.


I agree with you. I rarely find myself in a situation where a piece of async code forces me to refactor a synchronous code to be async.

A lot of junior devs I've worked with don't understand that putting `async` in front of a function doesn't actually make it asynchronous.


> A lot of junior devs I've worked with don't understand that putting `async` in front of a function doesn't actually make it asynchronous.

of course it does. annotating any function with async makes it implicitly return a Promise, which fundamentally changes how all callers have to use it (and their caller's callers, etc.). you can't "just" make a function async and change nothing about how it was used previously.

https://jsfiddle.net/om3tj2rd/

  async function foo() {
    return 2;
  }
  
  console.log(foo());
this


> of course it does.

i should clarify a bit, that this can still freeze your UI if foo() is expensive, since the microtask still runs on the same event loop. my point is that you cannot always throw async in front of a function and not change any other code.


> you might be better off just offloading that optimized synchronous op to a worker thread (or to wasm) to not block the UI.

It works in principle, but note that this really complicates your build process. In particular, if you're writing a library that other people will use as a dependency, there's really no good way to use workers at all without affecting how people bundle their code using your library.


The library use case is trickier, but bundlers do a pretty good job of handling workers (albeit with funky magic comments in webpack’s case).

What I find a pain is the uneven support for shared workers.


> you might be better off just offloading a synchronous op to a worker thread to not block the UI.

I believe it should be the answer. If your computations are tolerably fast, then you could do it without async, but if they are not, then it is better to use preemptive multitasking for them. The overhead on the kernel scheduler will be small, because you don't start 10k of threads concurrently eating CPU time. Probably the overhead of starting a thread doesn't matter either with long tasks. As a bonus you could also do blocking i/o operations without thinking about blocking.


Is this a JS specific issue? I find python is decently friendly to having little pockets of async where it makes sense in what is otherwise a regular synchronous program.


I'd say it's the other way around. In JS, async is just syntax sugar on Promises, they still execute within the same event loop. So regardless of if you are in async or not, you always have to think about not blocking. This becomes a lot easier to reason about, because all code is from the beginning made to be non blocking. Whereas in python if you call a blocking sync function from async world you are up for trouble. The problem solved in the OP is the unusual case where you need to do some big sync computation.


My experience is that Python is worse at async than JavaScript. At least, the debugging experience quickly scales up to nightmarish.


Does anyone know when we went from calling things serial and parallel execution to sync and async?

Was it before or after we started calling "man-hours" "story points"?


Async and parallel are a bit different.

Parallel means that things execute at the same time (or at least appear to do so). Async means that you yield control to some kind of scheduler and get it back at a later point.

Barring workers, JavaScript actually guarantees that two pieces of code never execute at the same time. That's the run-to-completion semantics.

When async was introduced to JavaScript from two different angles (callbacks in Node, Promise then await in browsers), there was limited parallelism involved (typically running I/O in the background), but the async was meant to ensure that:

1. while a task was (almost explicitly) waiting, another one could run;

2. in the case of the browser, we could yield control from JavaScript often enough that the UI could run smoothly.


Node didn’t introduce callbacks to JavaScript, they were present in the earliest browser APIs (img.onload, etc.)


Fair enough, I meant CPS-style programming.


These were just regular events on the UI thread, not any different from onclick etc., IINM.


So, the same thing as callbacks in Node.


When people talk about a callback in this kind of context they usually mean one function passed as an argument to another, in order to be invoked with the latter function's return value. Not event handlers like onclick, etc.


I’m not familiar with Node, but static event handlers aren’t usually referred to as callbacks in that context. “Callback” implies that something is being called, for it to call back to the client code. That’s not what’s happening with the onxxx event handlers. There is no “back” there. The event loop simply calls “forward” into the JavaScript code.


I don’t know what you mean by “forward” because there is no difference between these two:

  img.onload = function() {…}
  img.src = “some url”

  img.load(“some url”, function() {…})
The early JavaScript APIs use the first style, but the result is the same.


Async and parallel are not the same thing. You can run code async and yet not parallel (one core, no i/o).


Async in JS is not in parallel, which is a very -very- important distinction for program correctness.


JS, as typically run in a browser, is both async and single-threaded.


Parallel code runs parallel at the same time (on different CPU cores etc.)

Async code is scheduling different parts after each other. It's still running in a single thread (maybe on same core)

aaync essentially is non-blocking IO in many cases. It can also ahieldnsome logic running in a diffefent thread, hidden from that segment of code.


I think your explanation of async, while true, doesn't get at what's special about async. The explanation seems true of (non-parallel) concurrency as well (for example, thread scheduling on a one-core CPU).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: