Hello,
while working on the promise memory-leak problem 
<https://github.com/promises-aplus/promises-spec/issues/179> I noticed the 
following in the current ES6 spec:

```js
var count = 0,
    nonThenable = {get then() { count++; return undefined; }};

function GetResult(n) {
    if (n <= 0)
        return nonThenable;
    return Promise.resolve().then(()=> // just for a little delay
        GetResult(n-1);
    );
}
GetResult(5).then((res) => {
    assert(res == nonThenable);
    assert(count == 1); // !!!
})
```
Since we only *resolved* a promise once with `nonThenable` (the promise that is 
returned from `GetResult(1)`), I expect that `.then` is only accessed once. The 
other 4 promises are *fulfilled* with it, and we know that [[PromiseResult]]s 
are never thenables. Or should not be, if it didn't change in the meantime.

I suggest to change the spec so that a Promise Resolve Function (25.4.1.3.2) is 
not called multiple times, even if multiple promises (that adopt each other) 
are fulfilled with it. This would pave the road to allow fixing memory leaks 
from long recursive adoption chains, so that "intermediate" promises are no 
longer considered and can be garbage-collected before the eventual resolution.

A possible change could be made by amending the sections 25.4.1.3 
CreateResolvingFunctions and 25.4.5.3 Promise.prototype.then like so:

* A promise resolve function is an anonymous built-in function that has 
[[Promise]], [[AlreadyResolved]] *and [[fulfillAdopter]]* internal slots.

* CreateResolvingFunctions would set the [[fulfillAdopter]] slot of `resolve` 
to a function like
      function fulfiller(resul) {
          if (! F.[[AlreadyResolved]]) {
              F.[[AlreadyResolve]] = true
              Return FulfillPromise(F.[[Promise]], result)
          }
      }

* Insert a step 5.5 in PerformPromiseThen, like
  If `onFulfilled` has a [[fulfillAdopter]] internal slot, set 
fulfillReaction.[[handler]] to onFulfilled.[[fulfillAdopter]]

So when a promise is adopted, and its `.then()` method is called, a `fulfill` 
handler is installed not a `resolve` handler. Which works as long as the 
invariant holds that result values are not thenable.

I haven't thought through the implications that this had on subclassing, but 
this is supposed to work even if `.then` was overwritten as long as it calls 
`super.then()` with the original resolve handler. (If it wrapped the resolve 
handler, we should resolve the value again indeed).

regards,
 Bergi 
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to