6 ms·
Nicely done, thanks! Still, having to explain a concept as simple as eventual computation reinforces my belief that promises as a whole is broken, and should be
by radiospiel 11y ago
Nicely done, thanks! Still, having to explain a concept as simple as eventual computation reinforces my belief that promises as a whole is broken, and should be done in something that looks like synchronous code with some help of the underlying runtime. (And no, ES7's async is not good enough for me here)
- Shog9 11y agoSounds like a leaky abstraction. Easier to get started with maybe, but woe betide the legion of programmers who make assumptions based on the appearance of synchronous execution only to be left stranded when those assumptions don't hold. Asynchronous execution isn't an unfortunate design choice to be papered over; better to make it as easy as possible to learn and work with.
- radiospiel 11y agoI am not calling to ignoring the async nature of computing in general, I am calling for better tooling. The Linux (and other OS'es) kernel handles I/O asynchronously, yet processes using open(2) + friends work quite well and are usually written in synchronous style. That the actual asyncness is hidden in kernel space does in general not lead to bad programs; and whoever needs async in the space of a single process can still use async tools provided by the kernel when needed. That tooling is much better as it leads to simpler code. I don't see ES7's async support being there yet. Elixir, Go, a number of others: much more.
- Shog9 11y agoApples and oranges. You're describing a platform where it's considered acceptable for programs to block on IO and jump through hoops in rare cases where that's absolutely not ok. A set of assumptions that maybe still made sense 30 years ago, and also a leaky abstraction, but one we've become accustom to working around. If you want that in JavaScript, you can have it; promises were created on the assumption that asynchronous logic is desirable as a matter of course.
- anonymoushn 11y agoWhat are you going to do in Javascript, though, if the I/O takes some time to happen? In golang or lua or whatever I can say "this execution context should block on the I/O for x seconds, then give up and return an error." (the other tens of thousands of execution contexts can keep going) In Javascript I would probably do... the same thing? But I would do it using promises?
- Osiris 11y agoSome Promise library add support for things like `.timeout` so you can force a promise to reject if the promise takes too long to resolve. In cases like a node.js server, Promises allow a single node instance to handle thousands of concurrent requests because the event loop isn't blocking waiting for a single IO request to complete, the process can happily do lots of other things while Promises are pending.
- anonymoushn 11y agoYes, I expect Promise libraries to support timeouts. So it's right that promises allow me to do the same thing I would do with sequential code in other languages? If I had the option of using coroutines when would I choose to use promises? Edit: I'm asking because the context of this thread is that one person said that sequential APIs for asynchronous operations, such as open(2), are pretty nice, and someone else said no they're not pretty nice and we should explicitly deal with the asynchronous nature of operations like open(2) by NOT writing sequential code there.
- Shog9 11y agoIf you have promises OR you have coroutines, your life is good. Either way, you don't have to choose between writing fragile sequential code and writing error-prone synchronization logic. If you don't have either, then you'll probably end up re-implementing one or the other eventually anyway.
- SerpentJoe 11y ago
- Matthias247 11y agoImho in this area there is really no general good solution: Either you use the leaky blocking IO abstraction over the async IO - and you will require multiple threads/tasks for doing anything more complicated and lots of complex synchronization. Or you use the async APIs directly and need to work with callbacks/promises/etc., but you can at least avoid synchronization. I personally prefer the async solution, and I think async/await is a good solution to make async code a little more readable.
- rdtsc 11y ago> Either you use the leaky blocking IO abstraction over the async IO What do you mean the leaky blocking IO. It is the non-blocking IO that is leaky, isn't it. If at the business logic there is a request being processed that needs to go through steps a,b,c and then return. b, can't be done unless a finishes and c unless b finishes is mapped pretty cleanly to an execution context of a thread/actor/goroutine/task/process. Shoving IO callback functions in between handling the steps, or spreading them across callbacks or using promises is leaking the abstraction of the platform not being able to handle concurrent processing very well.
- Matthias247 11y agoWhat I meant is that blocking IO is a leaking abstraction, because IO is always asynchronous. What blocking does is adding an extra step (start the process AND waiting for the result) and thereby hiding something. I agree that if you have some pure linear steps (do A, then do B, then do C, and probably use the result from the last step for the next), then the synchronous abstractions work fine. But as soon as you add various timeouts, different error handling strategies, multicast communication, cancellation and other stuff to your async IO procesing then it isn't purely linear anyway. I often end up implementing quite complex state machines for heavy IO related code, and for this I am more happy with working in an asynchronous single-threaded environment.
- deleted 11y ago[deleted]
- findjashua 11y agoi love elixir actors / golang goroutines, since i don't have to give any special treatment to async operations.
- chenglou 11y agoDoes elixir solve this problem? http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- anonymoushn 11y agoCan you clarify? In golang, by convention, you only ever use asynchronous functions that return values. So there is "only one color." (You also have the option of writing callback spaghetti or implementing promises, but why would you do that?) In elixir, you have the option of blocking on a Task. So you can do the same thing you do in golang, if you want. I don't know enough about elixir to say what the culture is like around this.
- fzzzy 11y agoYes. In elixir and erlang, you can call a function without knowing whether it will wait on an asynchronous message. The function you called will not return until the message is received or a timeout is reached.
- Xixi 11y agoIt does. The problem is simply solved because Elixir has preemptive multitasking [1], and supports millions of processes on a simple server. So let say you have a blocking function fetch_data_sync, and want to execute it in a non blocking way: just spawn a process: task = Task.async(fn -> fetch_data_sync() end) do_some_other_work() Task.await(task) |> do_something_with_data() So you can mix 'red' and 'blue' functions as much as you want. Problem solved. [1] Erlang/Elixir multitasking is often called preemptive, but in fact it is a little bit more subtle than that. It is however a good first approximation when writing code.
- 11y ago
- ashark 11y agoNodeJS: you needed 5% of your code to be async, so we made everything async so you get to write awkward async-but-not-really code for the other 95%, too. That's better, right? At least that's how it's always felt when I've used it for anything non-trivial yet well within its typical set of use cases.
- taurath 11y agoIn a typical web API in node that I've seen, by function count I'd say around 60-70% of code is asynchronous.
- ashark 11y agoWell yeah, I usually end up writing lots of async code because you pretty much have to since all the libraries assume that's what you want, but shoehorning in dependencies (promises, callbacks, callbacks mutated into promises by a promises library—it's a mess) until it could have just as well been written as at most two threads. So it's async in pattern but not in fact.
- taurath 11y agoMost of the time that IS what you want in general in node, as if you start running anything with even moderate CPU time (let alone a call to an external service) you block the event loop and chug the system. The big performance gain you get with node in the form of max simultaneous connections per clock you get from not blocking. Most (95%+) of packages I've seen use callbacks, and maybe ~10% expose promise interfaces even if they use them internally.
- dboreham 11y agoDespite the downvotes, you are absolutely correct. My feeling, after working on a moderately complex server application in Node is that it was designed for only very simple applications that can be done with one level of callback. I find the "it works this way because its better" cult quite irritating. It works that way because the #*(^#$&^ JS interpreter isn't multithreaded.
- davexunit 11y agoWhat JavaScript needs (and other languages need, too) are first-class continuations. First-class delimited continuations to be precise. If you have those, you can implement whatever control flow you'd like, such as coroutines.