Javascript - 批量处理数组并显示进度

时间:2017-04-28 04:55:16

标签: javascript settimeout

亲爱的Javascript Guru&#39>:

我有以下要求:

  1. 以1000(或任意大小)的批量处理大型数组。
  2. 处理每个批次时,请更新UI以显示我们的进度。
  3. 处理完所有批次后,继续下一步。
  4. 例如:

    function process_array(batch_size) {
    
       var da_len = data_array.length;
    
       var idx = 0;
    
    
       function process_batch() {
    
          var idx_end = Math.min(da_len, idx + batch_size);
    
          while (idx < idx_end) {
    
             // do the voodoo we need to do
    
          }
    
       }
    
       // This loop kills the browser ...
    
       while (idx < da_len) {
    
          setTimeout(process_batch, 10);
    
          // Show some progress (no luck) ...
    
          show_progress(idx);
    
       }
    
    }
    
    // Process array ...
    
    process_array(1000);
    
    // Continue with next task ...
    // BUT NOT UNTIL WE HAVE FINISHED PROCESSING THE ARRAY!!!
    

    由于我是javascript的新手,我发现一切都是在一个线程上完成的,因此,需要在处理和更新UI方面获得一些创意。我找到了一些使用递归setTimeout调用的例子,(一个关键的区别是我必须等到数组完全处理后再继续),但我似乎无法按上述方式工作。

    另外 - 我需要一个纯粹的&#34; javascript解决方案 - 没有第三方库或使用Web工作者(不完全支持)。

    任何(和所有)指导都将不胜感激。

    提前致谢。

4 个答案:

答案 0 :(得分:1)

您可以从数组创建流并使用批处理流进行批处理,以便您可以批量流式传输到UI。

stream-array

batch-stream

答案 1 :(得分:1)

在JavaScript中,在HTML页面中执行脚本时,页面将变为无响应,直到脚本完成。这是因为JavaScript是单线程。

您可以考虑在后台运行的JavaScript中使用Web工作器,而不影响其他脚本,而不会影响页面的性能。

在这种情况下,用户可以继续在UI中做任何他想做的事。

您可以发送和接收来自网络工作者的消息。

More info on Web Worker here

答案 2 :(得分:0)

因此,递归的魔力之一就是考虑你需要传递的东西,以使其有效。

在JS(和其他功能语言)中经常涉及函数。

function processBatch (remaining, processed, batchSize,
                       transform, onComplete, onProgress) {
  if (!remaining.length) {
    return onComplete(processed);
  }
  const batch = remaining.slice(0, batchSize);
  const tail = remaining.slice(batchSize);

  const totalProcessed = processed.concat(batch.map(transform));
  return scheduleBatch(tail, totalProcessed, batchSize,
                       transform, onComplete, onProgress);
}

function scheduleBatch (remaining, processed, batchSize,
                        transform, onComplete, onProgress) {
  onProgress(processed, remaining, batchSize);
  setTimeout(() => processBatch(remaining, processed, batchSize,
                                transform, onComplete, onProgress));
}

const noop = () => {};
const identity = x => x;

function processArray (array, batchSize, transform, onComplete, onProgress) {
  scheduleBatch(
    array,
    [],
    batchSize,
    transform || identity,
    onComplete || noop,
    onProgress || noop
  );
}

这可以极其简化,而现实是我在这里只是有点乐趣,但是如果你遵循这条路径,你应该在一个封闭的系统中看到递归,该系统适用于任意对象,任意对象,任意数组长度,完成时任意代码执行,以及每个批处理完成并安排下一次运行时。

说实话,您甚至可以通过更改3行代码来替换自定义调度程序,然后您可以记录您想要的任何内容......

const numbers = [1, 2, 3, 4, 5, 6];
const batchSize = 2;
const showWhenDone = numbers => console.log(`Done with: ${numbers}`);
const showProgress = (processed, remaining) =>
  `${processed.length} done; ${remaining.length} to go`;

const quintuple = x => x * 5;

processArray(
  numbers,
  batchSize,
  quintuple,
  showWhenDone,
  showProgress
);

// 0 done; 6 to go
// 2 done; 4 to go
// 4 done; 2 to go
// Done with: 5, 10, 15, 20, 25, 30

过度破坏?哦,是的。但值得熟悉这些概念,如果你要花一些时间用这种语言。

答案 3 :(得分:0)

谢谢大家的意见和建议。

以下是我确定的代码。该代码适用于任何任务(在我的情况下,处理数组),并在需要时为浏览器提供更新UI的时间。

“do_task”函数通过setInterval启动一个匿名函数,该函数在两个步骤之间交替 - 批量处理数组并显示进度,这一直持续到数组中的所有元素都被处理完毕。

  function do_task() {

     const k_task_process_array = 1;

     const k_task_show_progress = 2;


     var working = false;

     var task_step = k_task_process_array;

     var batch_size = 1000;

     var idx = 0;

     var idx_end = 0;

     var da_len = data_array.length;


     // Start the task ...

     var task_id = setInterval(function () {

        if (!working) {

           working = true;

           switch (task_step) {

              case k_task_process_array:

                 idx_end = Math.min( idx + batch_size, da_len );

                 while (idx < idx_end) {

                    // do the voodoo we need to do ...

                    }

                    idx++;

                 }

                 task_step = k_task_show_progress;

                 working = false;

                 break;

              default:

                 // Show progress here ...


                 // Continue processing array ...

                 task_step = k_task_process_array;

                 working = false;

           }


           // Check if done ...

           if (idx >= da_len) {

              clearInterval(task_id);

              task_id = null;

           }

           working = false;

        }

     }, 1);

  }