下面的模式有什么区别来调用返回promise的递归JavaScript函数?

时间:2017-11-30 23:57:49

标签: javascript recursion promise

在包含100000条记录的以下代码中,recursiveFnReturnsPromiseV1执行正常,而recursiveFnReturnsPromiseV2因堆栈外异常而失败。两者之间的唯一区别是承诺被递归的方式。在v1中,递归在第一个promise的“then”内,而在v2中,递归在原始promise本身内。有什么区别?

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>

1 个答案:

答案 0 :(得分:1)

让我们剥离Promise constructor antipattern,然后使用简单的Promise.resolve。您的功能现在变为

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));
}

现在应该很清楚为什么第二个函数会溢出堆栈。

为什么第一个也不是?因为递归调用是在异步 then回调中,这意味着它在稍后(在外部调用返回之后)使用新的调用堆栈启动。