
JavaScript's Promise Is Memory Unsafe - bad_user
https://alexn.org/blog/2017/10/11/javascript-promise-memory-unsafe.html
======
chrismorgan
I think we’re operating on different definitions of “memory unsafe”. Memory
unsafe means accessing uninitialised memory, freed memory, other objects’
memory and so forth.

This article seems to simply be describing memory _inefficiency_ in promises,
that in certain situations it can leak like a sieve.

If Promise in any particular environment was not memory-safe, that would
probably be a high or critical security vulnerability. Mere memory leaks are
not a security issue.

(I’m operating on the reasonable assumption that the JavaScript engine is
simply going OOM and crashing for that reason instead of something more
sinister.)

~~~
vbezhenar
As I understood, problem is that stack overflow happens in native code, which
causes access violation error and virtual machine is killed. "Proper" stack
overflow should result with JavaScript exception and virtual machine should
remain in working state, the same is about memory leak.

~~~
narag
_...which causes access violation error and virtual machine is killed_

When you write "virtual machine" do you mean the local JavaScript engine? For
most people that would be just a little annoyance, the usual browser crash. I
actually prefer crash to freeze.

~~~
vbezhenar
> When you write "virtual machine" do you mean the local JavaScript engine?

Yes, of course, JavaScript VM inside a browser process or one of them.

> For most people that would be just a little annoyance, the usual browser
> crash. I actually prefer crash to freeze.

Any browser crash is a bug and should never happen for any input. If browser
is smart enough to run separate process for different pages, this is little
annoyance indeed. I remember when crashed page caused entire browser to crash
and often lose open tabs. That wasn't little annoyance. Also some clever guy
might figure out how to exploit this crash, I'm not sure if it's possible, but
I'm not the clever one :)

------
weddpros
His code is creating an infinite promise chain. Did the author really expect
it would not eat all his memory and crash?

If the author had written the same code with callbacks, a stack overflow would
have been thrown.

So misunderstanding of promises (and/or async coding) is the problem here, not
promises.

~~~
bad_user
> _His code is creating an infinite promise chain. Did the author really
> expect it would not eat all his memory and crash?_

Processing an infinite or really long promise chain is what ends up happening
when you're working with streams and functional programming in general.

Yes, I expected it to not leak memory, because sane implementations don't
leak. One such implementation is Scala's Future [0], using an implementation
trick which I then ported in Funfix.org, so it's definitely possible.

> _If the author had written the same code with callbacks, a stack overflow
> would have been thrown._

That's not true. For one, asynchronous calls will not trigger stack overflows.
And a callback approach won't leak heap memory either, because you don't have
to return a value-wannabe to the caller, so you're not building a _stack_.

> _So misunderstanding of promises (and /or async coding) is the problem here,
> not promises._

I'm involved in no less than 3 libraries [1] [2] [3] implementing Promise and
Haskell IO alternatives in JavaScript and Scala, so I'm pretty sure that I
understand promises by now :-)

I'm also pretty fond of this article I wrote a while back, asynchrony being
sort of a fetish of mine: [https://alexn.org/blog/2017/01/30/asynchronous-
programming-s...](https://alexn.org/blog/2017/01/30/asynchronous-programming-
scala.html)

[0] [http://www.scala-
lang.org/api/2.12.3/scala/concurrent/Future...](http://www.scala-
lang.org/api/2.12.3/scala/concurrent/Future.html)

[1] [https://monix.io](https://monix.io)

[2] [https://github.com/typelevel/cats-
effect](https://github.com/typelevel/cats-effect)

[3] [https://funfix.org](https://funfix.org)

~~~
weddpros
My bad, I was probably a bit aggressive and didn't read through. Thank you for
correcting a few points.

However, I'm still convinced that calling Promises "memory unsafe" is largely
incorrect:

\- if it wasn't for the setImmediate, I insist the callback version would
cause a stack overflow

\- you can't create an infinite number of objects (promises in this case)
linked to each other without expecting a memory overflow. If it works in the
Future example, it means the Futures are somehow not connected to each other,
which must violate the contract for Promises. Can a Future object be
resolved/flatMapped more than once without rerunning, ie. does it cache its
value? I suspect this is the difference between a Promise and a Future...

\- "You can't have infinite loops with Promises" would make a better title

I'm sorry, however, for trolling.

Cheers

------
bad_user
Article has been buried soon after hitting the front page.

Given that it has more points and it is newer than some articles on the front
page right now, I'm guessing I've hurt people's feelings.

As one of its long time visitors and contributors, I am disappointed by HN,
but that's not news.

~~~
gotofritz
~~ hug ~~

------
yuchi
Interesting article but has nothing to do with «Unsafeness». (better wording:
Promise implementations leak on the heap)

~~~
sirclueless
It's not even a particularly unexpected leak. When you create unbounded
recursion, you leak memory. This is true if you don't use Promises, and
remains true when you do.

This is really just another complaint about lack of Tail-Call Optimization.
This issue has come up for any number of languages and libraries. Often I
think that the "O" in TCO is a misnomer. If it was really an "Optimization" it
wouldn't affect correctness of the program, but there are many cases where a
program is correct if TCO is happening but undefined if not. It's more akin to
lazy/strict evaluation: equivalent in simple cases, a major correctness issue
in more complex ones.

~~~
bad_user
> _This is really just another complaint about lack of Tail-Call Optimization_

TCO needs to be supported by the runtime, but what I'm talking about only
needs to be supported by the Promise implementation, so in this case it's just
a sloppy library we are talking about.

> _When you create unbounded recursion, you leak memory_

This is only true in weird universes, like that of JavaScript.

It's actually really easy to end up with a really long chain of tail recursive
calls - for example when processing streams or when doing functional
programming in general.

Having to explain why this is a huge problem is one reason for why I feel that
JavaScript's ecosystem is so broken. In case you're wondering, given Promise's
surprises, I actually think going for the _callback hell_ is saner, because in
such cases you just pass a callback around without surprises.

~~~
sirclueless
TCO is classically a compiler/runtime optimization, but that's only because
people didn't commonly implement fundamental control flow as libraries until
relatively recently. Whether it is the runtime introspecting function calls to
decide whether it can elide the current stack frame, or a futures library
introspecting return values to decide whether the current future can delegate
to the returned one, it's the same concept.

As for "weird universes", it is typically only functional programming
languages that guarantee TCO as part of their standard. Most programmers live
in a world where they can't rely on their implementation to optimize tail-
calls, which is to say they don't make unbounded recursive calls. If they do,
they end up with an insidious form of non-portable code.

