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

> A non-async function is "regular logic", it must complete without blocking.

Maybe one can enforce this convention in the particular project, but there's no ecosystem-wide consensus on this, and in fact I don't want this to be consensus. I write blocking non-async functions every day. Why am I wrong to do so?



There is consensus in some ecosystems. Javascript absolutely maintains that invariant. There are (almost) no blocking functions in the javascript / node standard libraries and we work hard to keep it that way. Go maintains similar discipline at the OS syscall level.

I feel like the "what color is your function" thing is incomplete. There are arguably 3 types of functions:

- Functions which do all their work synchronously and return without blocking

- Async functions which contain an internal state machine

- Functions which block on expensive IO or long computations

Mixing blocking functions and async functions in the same kernel thread leads to various performance disasters. Javascript is so meticulous about not having blocking IO in part because its basically impossible to tell from a function's signature whether it will block the thread. Lua has this problem - callback oriented lua feels like a natural fit for the language, but lots of 3rd party libraries are packed with blocking calls. Writing asyncronous lua feels like fighting a river. You have to constantly guard against calling blocking code, and most API docs won't tell you where they block.


We are talking about Rust. There is no consensus in Rust.


I think the OP was using examples from a different ecosystems to demonstrate how such a consensus could be reached, if people wished for it.


I don't wish it. That sounds terrible to me.


I think your comments have made that clear. Which is fair enough. But I imagine others feel differently.


I have seen fs.*Sync functions being called deep in async-land more than once or twice in Node.


Those methods were added super early (node 0.2 or something) and can’t be removed because of backwards compatibility. Many of the core node team think they should never have been added - for that exact reason.


> I write blocking non-async functions every day. Why am I wrong to do so?

This (in my opinion) not "wrong". At least not in general. There are instances where it might be more or less probematic though.

It's probably problematic if you already have a bunch of async code in the codebase, because other readers of the code are likley to expect blocking functions to be async.

It's maybe problematic for high performance or high scale code. Synchronous blocking functions are more likely to hit OS limits (file handles, network sockets, etc) than async code. If the code is obviously written from the ground up for high scale/performance, this is less likely to be a problem, but if it's proof of concept code that's likely to get pushed into production by over eager PMs as soon as it passes tests, it'll be worse.

It's possibly problematic if done in a language/frameworks where async is idiomatic - it'd be wrong to write using blocking functions in a nodejs codebase, because you'd be breaking other people expectations when reading/understanding the code.

Maybe a useful rule of thumb might be "if more than some number (perhaps 30 or 50%) of other people working on the code might think 'hang on, I'm gonna refactor this to use async', then maybe using a non-asyn blocking function was the wrong choice. That means it's _never_ the wrong choice for one person codebases. It means it's probably almost always a wrong choice in a javascript codebase. For everything else? "It depends". I'd always choose to go with "the principle of least astonishment" - do whatever other people who might be affected would expect you to wherever possible.


I believe the idea is that within a project that uses async functionality, you should only use non-async functions when the logic does not call blocking functionality. If you are mixing async functionality and synchronous functions with/blocking I would consider the latter a defect unless it is handled properly within an asynchronous context.


I really don't understand the logic of this on a multi-threaded system. The vast majority of functions I write are best executed synchronously, the remainder is usually composed of logic wrapping heavy computations which can be executed in parallel or logic surrounding I/O which can be executed concurrently.

An async system which poisons the rest of my code to force async usage doesn't seem like it will scale to code leveraging multiple libraries and will likely fail at the first lib where the author decided not to bother. The beauty of coroutines in go and Java(soon) is that the async functionality remains local to the code that can make use of it - everyone else just sees a thread-like API.


I think you're right on one level: if your codebase is pervasively, implicitly multithreaded, then there's little value in explicitly marking yield points. But if your codebase is pervasively, implicitly multithreaded, then it's impossible to maintain without locking everywhere (and difficult even then), and combining async with (blocking) locking does not work well.

In a codebase where concurrency is carefully controlled and constrained, an async system that gives you visibility into where the yield points are is very valuable: https://glyph.twistedmatrix.com/2014/02/unyielding.html .


> An async system which poisons the rest of my code to force async usage doesn't seem like it will scale to code leveraging multiple libraries and will likely fail at the first lib where the author decided not to bother.

You are quite correct. This happens.


> logic surrounding I/O which can be executed concurrently.

That's the bit you'd use an async runtime for, in a (mostly) dedicated thread.

The heavy computations would be in other threads that aren't doing so.




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

Search: