用于在JavaScript ES6中对大数据进行异步迭代的习惯用法

时间:2015-12-28 11:53:27

标签: javascript ecmascript-6

是否存在在ES6中迭代大型数据集以避免浏览器超时的习惯用法?

假设我需要做一些像生成1600万个立方体或其他东西的东西,并且直接循环会超时浏览器。

function generateCubes(num) {
  var cubes = [];
  for (var ii = 0; ii < num; ++ii) {
     cubes.push(generateCube());
  }
  return cubes;
}

var cubes = generateCubes(16000000);

所以我可以把它变成像这样的异步回调

function generateCubes(num, callback) {
  var maxPerIteration = 100000;
  var cubes = [];

  function makeMore() {
    var count = Math.min(num, maxPerIteration);
    for (var ii = 0; ii < count; ++ii) {
      cubes.push(generateCube());
    }
    num -= count;
    if (count) {
      setTimeout(makeMore, 0);
    } else {
      callback(cubes);
    }
  }
  makeMore();
}

但遗憾的是我突然要重构我的所有代码

generateCubes(16000000, function(cubes) {
   ...
   // all the code that used to be after cubes = generateCubes
});    

我可以将其转化为基于承诺的东西,但这只会增加样板量。

在任何一种情况下,我都认为我可以写一个通用版本

function generateThings(factory, num, callback) {
  var maxPerIteration = 100000;
  var things = [];

  function makeMore() {
    var count = Math.min(num, maxPerIteration);
    for (var ii = 0; ii < count; ++ii) {
      things.push(factory());
    }
    num -= count;
    if (num) {
      setTimeout(makeMore, 0);
    } else {
      callback(things);
    }
  }
  makeMore();
}

在这种特殊情况下,我正在生成1600万件事,这是一种迭代。也许接下来我想迭代这些事情。

 function forEachAllThThings(things, op, callback) {
   var maxPerIteration = 100000;
   var num = things.length;

   function doMore() {
     var count = Math.min(num, maxPerIteration);
     for (var ii = 0; ii < count; ++ii) {
       op(things[ii]);
     }
     num -= count;
     if (num) {
       setTimeout(makeMore, 0);
     } else {
       callback();
     }
   }
   doMore();
}

还有更多的ES6方式可以做到更简洁或更通用吗?

注意:请不要挂断生成多维数据集。那不是问题。此外,它不仅仅是关于超时问题,它也可能是一个混乱的问题。例如,我曾经在一个需要反序列化场景图的项目中工作过。中等复杂的图形可能需要5-10秒来反序列化(变成对象)。在那5-10秒内,浏览器被冻结了。

解决方案类似于上面的forEachAllTheThings,因为我们只读取每个tick的N个对象,以免锁定浏览器。这是所有自定义代码。我只是想知道是否有一些新的ES6功能提供了任何类型的简化,解决了在多个 ticks 上做大量工作的问题,就像它们似乎简化了异步代码一样(因为这是感觉是一种异步代码形式)

更新

根据@ Bergi关于宣传setTimeout的建议,我认为这就是所建议的。

// returns a Promise that resolves in `time` millisecond
function sleep(time) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve, time);
  });
}

// returns a promise that resolves to an array of things
function generateThings(factory, num) {
  var maxPerIteration = 100000;
  var things = [];

  function makeMore() {
    var count = Math.min(num, maxPerIteration);
    for (var ii = 0; ii < count; ++ii) {
      things.push(factory());
    }
    num -= count;
    return num ? sleep(0).then(makeMore) : things;
  }

  // we need to start off with one promise
  // incase num <= maxPerIteration
  return Promise.resolve(makeMore());
}

function generateCube() {
  return Math.random();  // could be anything
}

generateThings(generateCube, 300000)
.then(function(things) {
  console.log(things.length);
});

我认为,如果您的代码中已经有sleep,那么它会略微ES6化并且会有几行变小(这似乎是一个合理的假设)。

1 个答案:

答案 0 :(得分:2)

我可能会将多维数据集的生成卸载到web worker,它不会出现超时问题,假设多维数据集只包含JavaScript基本类型,因此可以发布到准备好后的主UI线程。理想情况下,多维数据集将为transferrable objects,因此您不必克隆它们,而是传输它们,从工作线程到主UI线程。