具有超时的递归函数,以防止超出堆栈调用



我正在尝试使用递归函数查找任何深度的任何对象的任何和所有图像,但正如预期的那样,它会在某些应用程序中给出最大调用堆栈大小超出错误。这里的解决方案说将递归函数包装在一个setTimeout中,但随后该函数似乎不再工作了。

const resultsReg = []
const resultsTimeout = []
const obj = {
    key : {
        foo: 'bar.jpg'
  }
}
function findImages(object, results) {
  for (var key in object) {
    if (typeof object[key] === 'string') {
      if (object[key].match(/.(jpg)$/i) && !results.includes(object[key]) && results.length < 9) results.push(object[key]);
    }
    else if (typeof object[key] === 'object') {
      findImages(object[key], results); // this works, but in some applications will give Maximum call stack size exceeded error
    }
  }
}
function findImagesTimeout(object, results) {
  for (var key in object) {
    if (typeof object[key] === 'string') {
      if (object[key].match(/.(jpg)$/i) && !results.includes(object[key]) && results.length < 9) results.push(object[key]);
    }
    else if (typeof object[key] === 'object') {
      setTimeout(function() {
        findImagesTimeout(object[key], results) // this fails
      }, 0)
    }
  }
}
findImages(obj, resultsReg)
findImagesTimeout(obj, resultsTimeout)
console.log(resultsReg)
console.log(resultsTimeout)

输出:

[ 'bar.jpg' ]
[]

我做错了什么吗?

为了避免Maximum call stack size exceeded error,您可以使用蹦床函数,这将防止堆栈溢出。您基本上不会直接执行递归调用添加另一个堆栈帧,而是返回一个将处理执行的包装器函数。

下面是一个示例。

const resultsReg = []
const obj = {
  key : {
    foo: 'foo.jpg',
    key: {
      bar: 'bar.jpg',
      key: {
        baz: 'baz.jpg'
      },
      point: {
      	x: 32,
        y: 64
      }
    },
    url: 'google.com',
  }
}
function trampoline (func, obj, reg) {
  var value = func(obj, reg);
  while(typeof value === "function") {
    value = value();
  }
  return value;
}
function findImages (obj, reg) {
  for (var key in obj) {
    if (Object.prototype.toString.call(obj[key]) === '[object Object]') {
      return function () {
        return findImages(obj[key], reg);
      }
    } else {
      if (obj[key].match(reg)) {
        // Here you can start to push.
        resultsReg.push(obj[key])
      }
    }
  }
}
trampoline(findImages, obj, /.(jpg)$/);
console.log(resultsReg)

我认为在这种情况下您的超时毫无用处。

https://jsfiddle.net/f9c185p2/

function findImagesTimeout(object, results) {
  for (var key in object) {
    if (typeof object[key] === 'string') {
      if (object[key].match(/.(jpg)$/i) && !results.includes(object[key]) && results.length < 9) results.push(object[key]);
    }
    else if (typeof object[key] === 'object') {
      findImagesTimeout(object[key], results);
    }
  }
}

这个技巧不起作用,因为你在一个for循环中,所以解析器将无法清除堆栈,并且很可能会触发异常。

您可以跟踪要在变量中调用的函数,并在循环结束时调用它们。

像这样的东西

function findImagesTimeout(object, results) {
  var fn = []; //keep trace of the functions to call
  for (var key in object) {
    if (typeof object[key] === 'string') {
      if (object[key].match(/.(jpg)$/i) && !results.includes(object[key]) 
      && results.length < 9) 
       results.push(object[key]);
    }
    else if (typeof object[key] === 'object') {
      fn.push(function(key) {
        return function(){
         findImagesTimeout(object[key], results)
        }
      }(key));
    }
  }
  setTimeout(function(){
    fn.forEach(function(singleFn){
      singleFn();
    });
  }, 0);
 }
}

最新更新