0

In following code with 100000 records recursiveFnReturnsPromiseV1 executes fine while recursiveFnReturnsPromiseV2 fails with out of stack exception. The only difference between two is the way promises are being recursed. In the v1, the recursion is within a "then" of the first promise while in v2, the recursion is within the original promise itself. What's the difference?

let aValues = Array(100000);


recursiveFnReturnsPromiseV1(aValues, 0).then(function() {
  let p = document.createElement('div');
  p.innerText = 'recursiveFnReturnsPromiseV1 finished';
  document.getElementById('v1').appendChild(p);
}, function() {
  let p = document.createElement('div');
  p.innerText = 'recursiveFnReturnsPromiseV1 failed';
  document.getElementById('v1').appendChild(p);
})

recursiveFnReturnsPromiseV2(aValues, 0)
  .then(function() {
    let p = document.createElement('div');
    p.innerText = 'recursiveFnReturnsPromiseV2 finished';
    document.getElementById('v2').appendChild(p);
  }, function() {
    let p = document.createElement('div');
    p.innerText = 'recursiveFnReturnsPromiseV2 failed';
    document.getElementById('v2').appendChild(p);
  })

function recursiveFnReturnsPromiseV1(pValues, ix) {
  if (pValues.length <= ix)
    return Promise.resolve();

  return new Promise(function(c, e) {
    document.getElementById('v1').innerText = ix + 'v1' + Date.now();
    c();
  }).then(function() {
    return recursiveFnReturnsPromiseV1(pValues, ++ix);
  })



}

function recursiveFnReturnsPromiseV2(pValues, ix) {
  if (pValues.length <= ix)
    return Promise.resolve();


  return new Promise(function(c, e) {
    document.getElementById('v2').innerText = ix + 'v2' + Date.now();

    recursiveFnReturnsPromiseV2(pValues, ++ix).then(function() {
      c();
    }, function(err) {
      e()
    });
  })
}
<div id='v1'></div>
<div id='v2'></div>
smile.al.d.way
  • 361
  • 5
  • 17
  • 1
    v1, the recursion is done asynchronously (it's in a `.then`) ... v2 the recursion is not ... the code in the Promise constructor is synchronous ... note: your code is hard to read due to seemingly random indentation – Jaromanda X Dec 01 '17 at 00:01
  • Do you really find code readable that uses only one space of indent per level? I do not. Can't read it. Can't understand it as shown here. Will not spend any time on questions where the code is this hard to read. – jfriend00 Dec 01 '17 at 00:25
  • 1
    @jfriend00 - if you look at `recursiveFnReturnsPromiseV2` function, it's not even that, first code is indented by 4 .. then the return is indented by 2, then the first line of the promise executor is also indented by 2, then the next two lines are indented by 1, so "outdented" by 1 with respect to the block they are in ... as I mentioned, it's actually quite random (though it's easy to copy/paste into firefox scratchpad and fix :p ) – Jaromanda X Dec 01 '17 at 00:46
  • 1
    @JaromandaX - There are so many questions here we can spend our time on. If the OP isn't going to make their question readable, then I will likely just spend my time on the ones that do. Yeah, I could copy the code somewhere (like into a code formatter) to make it readable (which I do sometimes), but I'm offering the OP some advice on how to make their questions more attractive for people to spend their time on. If we don't provide feedback, they have no way to hear that they'd attract more help if they made their questions easier to deal with. – jfriend00 Dec 01 '17 at 00:50
  • 1
    @jfriend00 fair call, I guessed the source of the issue before even reading the code :p – Jaromanda X Dec 01 '17 at 00:53

1 Answers1

1

Lets strip away the Promise constructor antipattern, and use a simple Promise.resolve instead. Your functions now become

function recursiveFnReturnsPromiseV1(pValues, ix) {
  if (pValues.length <= ix)
    return Promise.resolve();

  let p = document.createElement('div');
  p.innerText = ix + 'v1' + Date.now();
  document.getElementById('v1').appendChild(p);

  return Promise.resolve().then(function() {
    return recursiveFnReturnsPromiseV1(pValues, ++ix);
  })
}

function recursiveFnReturnsPromiseV2(pValues, ix) {
  if (pValues.length <= ix)
    return Promise.resolve();

  let p = document.createElement('div');
  p.innerText = ix + 'v2' + Date.now();
  document.getElementById('v2').appendChild(p);

  return Promise.resolve(recursiveFnReturnsPromiseV2(pValues, ++ix));
}

It should be obvious by now why the second function overflows the stack.

Why doesn't the first as well? Because the recursive call is inside an asynchronous then callback, which means it starts later (after the outer call returned) with a fresh call stack.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375