Hacker News new | ask | show | jobs
by IIIIIIIIIIIIIII 3049 days ago
This may get me in hot water here but...

I started working with JS promises specifically when they were barely available in a beta runtime. It took me over a year of working with them to really get a feel for them, now it's been far longer. That's because while you can "understand" the description and use it just fine, but a deeper comprehension and intuition takes much more time. I experimented a lot and insisted on writing my own helpers from scratch, without looking up other people's code, because I wanted to get a feeling for the details.

This article seems quite artificial to me, the problems mostly made-up.

I don't see the point of the first complaint. If you don't want to start right away chain it to something that it should wait for. If it should not wait, then it can start right away. Her writes "Functions rescue us in this case because functions are lazy." which I don't quite understand: what is he running through promises if not functions? Hi "betterFetch" example mixes synchronous and promise syntax - how about using async/await if you prefer the former? I admit though I don't quite get the point of that example.

I don't understand the whole "run a promise" idea either - because you don't "run a promise", that whole notion has nothing to do with what "promise" means. Just look at the word! It represents a (wrapped) future value. Where does the idea of "running it" come from? How do you "run" a (future) value?

You have a function and it is quite easy IMO: Using a promise you chain it to whatever you want to wait for. These days you can even use semi-synchronous syntax (async/await). "Running a promise" makes no sense to me, you run functions, and I don't see where the difficulty lies here?

The second point, cancellation, has been discussed very, very thoroughly - after all, this was on the table to be standardized. One of the issues he raises is the same as point one - if you have a chain it's automatic. The main issue of cancellation is that you have zero control over the actual asynchronous operation that the promise actually stands for - because this is controlled by the OS alone! If you started I/O, what does "cancelling the promise" mean?

1. If it is still waiting: If you don't want to run something make sure the previous step returns a rejected promise. You can easily "cancel the promise". Just let your promise function check something in the parent scope (via callback or it is in its lexical scope) when its chained function starts, and if that says "you are canceled" then don't do it. You can put such a check as a standalone function anywhere in the promise chain you created, just let that "amIcancelled()" function throw or return a rejected promise. The whole chain aspect is something that the article is missing.

2. If the code is already running: you cannot cancel the actual (OS controlled) asynchronous operation, nor can you cancel a running JS function (unless you use async/await see bottom paragraph).

I agree that promises are not perfect, but async/await - not mentioned at all! - makes it a bit easier for many people - as long as they don't forget one thing: Even if your functions now look like synchronous ones there is a fundamental difference: A synchronous JS function is never interrupted by any other code. An async function is suspended and other JS code gets to run in the middle of it when it encounters an "await". This is something new first introduced by generators, before that JS functions were atomic (now some are not).

4 comments

> you cannot cancel the actual (OS controlled) asynchronous operation

This is way too broad a claim. If I do something like:

  (sleep 1 ; echo "done") &
  kill $!
I'm pretty clearly "cancelling the actual (OS controlled) asynchronous operation". Now it may have done some sleeping at that point, and if you replace the sleep with something that has side-effects, then some of those side-effects may have occurred, but the operation is still being "cancelled".

Obviously it's not the case that every operation can be (meaningfully) cancelled, but some can. This is even more true when you consider that when you're using JS, you're generally way above the OS level. XMLHttpRequest has an abort() method for a reason: The underlying socket request may be queued up based on your browser's connection limits and, even if it's kicked off, your browser is going to have multiple opportunities to abort the process even if none of the underlying sub-operations can be preemptively cancelled.

> It took me over a year of working with them to really get a feel for them

I had a similar experience. It took quite a while for me to stop shooting my foot. My takeaway from that experience was that, while they do have certain advantages, Promises suck. Any abstraction that is so unintuitive that it takes beginners dozens or hundreds of hours to master is probably not an abstraction worth using - especially if it is supposed to be a primary feature of the language.

I had the same experience with the callback pattern. It literally took a whole year to grok. And I code almost every day. I'm now a ninja with callbacks. So it's a hard to motivate myself to learn Promises. Syncronious code is more easy to deal with, and you get concurrency by thread abstraction. But it will eventually bite you when you start to get double transactions eg line 1 checks if there's funds in the account, line two draws money, line 3 inserts good. But then another thread takes the money between line 1 and 2. And then the "single threaded" event loop actually becomes easier to deal with then making sure your code is "thread safe" with locks etc.
> Any abstraction that is so unintuitive that it takes beginners dozens or hundreds of hours to master is probably not an abstraction worth using

Javascript can not depend on anything like that, because it's a gatekeeper language. But that is too general a phrasing.

Post sounded like they picked up promises over the weekend and didn't like how it broke their C# mold.

Quite a few points didn't make any sense or simply showed misunderstanding around how and why promises are what they are.

> Eager, not lazy

Why does this even matter? It's an implementation detail that optimises for performance.

The outcome, eventual resolution, is all that really matters.

> No cancellation

These are not tasks, and covered elsewhere in the thread: I/O.

> Never synchronous

Its a promise, so that doesn't really make sense to complain about.

Alas it is solved with Async/await (which is just promises under the hood.)

> then() is a mix of map() and flatMap()

This one I can concede as it would be useful to have the option, or at least have them exposed.

I suspect then was simply kept because that's what bluebird or whatever it was at the time did.

The author is the original author of the streaming library xstream (similar in functionality to RxJS) and Cycle.js.

I believe he's thought a lot about this problem.

Cancelling the promise can be useful in fact to prevent doing computations that won't be used anyway. I tried to explain it in this comment: https://news.ycombinator.com/item?id=16386454