最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

jquery - How to process the big loop without freezing the browser using setTimeOut function in javascript? - Stack Overflow

programmeradmin1浏览0评论

I have created two buttons .One is named 'sync' and other is named 'async'.When i click 'sync' button ,it should process the big array using loops and it freezes the browser until the loop is pleted processing the larger array.When i press the 'async' button.it should process the same large array without freezing the browser.How to do this using setTimeOut function?

I have created two buttons .One is named 'sync' and other is named 'async'.When i click 'sync' button ,it should process the big array using loops and it freezes the browser until the loop is pleted processing the larger array.When i press the 'async' button.it should process the same large array without freezing the browser.How to do this using setTimeOut function?

Share Improve this question asked Aug 3, 2016 at 5:36 AmudhaVigneshwaranAmudhaVigneshwaran 1152 silver badges8 bronze badges 4
  • Try using Web Worker. – Rohan Veer Commented Aug 3, 2016 at 5:38
  • Perhaps not exactly what you're looking for (as you specifically mention setTimeout), but you might find this article on web workers helpful: html5rocks./en/tutorials/workers/basics – SimianAngel Commented Aug 3, 2016 at 5:39
  • In my view you can use SetTimeInterval , refer this link : stackoverflow./questions/6081443/… – Sunil Kumar Commented Aug 3, 2016 at 6:40
  • @RohanVeer Still web worker freezes the browser.I created a separate javascript file in which array of 100000 index was processed and printed on console.Though it runs in separate thread,wht it freezes the browser?:( – AmudhaVigneshwaran Commented Aug 4, 2016 at 10:58
Add a ment  | 

4 Answers 4

Reset to default 5

Using async/await this in ES7 this gets pretty trivial. Example of long loop that takes too long

function main() {
  const numOperations = 1000000000;
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

function doSomeOperation(v) {
  return v * 1.1 / (v + 1);
}

Example of using async/await to make it yield to the browser once in a while

async function main() {
  const numOperations =  1000000000;
  const iterationsPerChunk = 10000000;
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    if (i && i % iterationsPerChunk === 0) {
       await oneMoment();
    }
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

function doSomeOperation(v) {
 return v * 1.1 / (v + 1);
}

function oneMoment() {
  return new Promise(resolve => setTimeout(resolve));
}

Picking a good value for iterationsPerChunk might be harder. You could easily make some class that checks performance.now and only calls await when a certain amount of time has passed, say 1/2 a second or 1 second. Each call to setTimeout will yield for anywhere from 5ms to 20ms so you don't want to await too often but it does make it easy to use.

example using performance.now

async function main() {
  const numOperations =  1000000000;
  let sum = 0;
  let then = performance.now();
  for (let i = 0; i < numOperations; ++i) {
  
    // calling performance.now is slow
    // so only check every 1000 iterations
    if (i && i % 1000 === 0) {
      const now = performance.now();
      
      // have 0.1 second elapsed?
      if (now - then > 100) {
        await oneMoment();
        then = performance.now();
      }
    }
    sum += doSomeOperation(i);
  }
  console.log(sum);
}
main();

function doSomeOperation(v) {
 return v * 1.1 / (v + 1);
}

function oneMoment() {
  return new Promise(resolve => setTimeout(resolve));
}

Update:

I heard a rumor the time restraints on setTimeout might be removed because you can work around them by posting a message to yourself via postMessage.

You still have the same issue that postMessage does take some amount of time but it's generally microseconds or less.

const pause = (function() {
  let reqId = 0;
  const reqs = new Map();

  window.addEventListener('message', (e) => {
    const resolve = reqs.get(e.data);
    if (resolve) {
      reqs.delete(e.data);
      resolve();
    }
  });

  return _ => new Promise(resolve => {
    const id = reqId++;
    reqs.set(id, resolve);
    window.postMessage(id);
  });
})();

async function main() {
  const numOperations =  1000000000;
  const iterationsPerChunk = 10000;
  const startTime = performance.now();
  let sum = 0;
  for (let i = 0; i < numOperations; ++i) {
    if (i && i % iterationsPerChunk === 0) {
       await pause();
    }
    sum += doSomeOperation(i);
  }
  const elapsedTime = performance.now() - startTime;
  console.log(sum);
  console.log(`elapsedTime: ${(elapsedTime * 0.001).toFixed(2)}seconds`);
}
main();

function doSomeOperation(v) {
 return v * 1.1 / (v + 1);
}

function oneMoment() {
  return new Promise(resolve => setTimeout(resolve));
}

You can use a loop variable, like the following code. In this example, the function increases every element by 1. The timeout period is 1 millisecond.

    var currentIndex;
    function processNthElement(array) {
        if (currentIndex >= array.length)
        {
            //the whole array has been processed, do what you need to do
            //with the results here
            return;
        }

        //do what you want with the array element here
        array[currentIndex]++;

        currentIndex++;

        setTimeout(function () {
            processNthElement(array);
        }, 1);
    }
    function processArrayWithSetTimeout(array) {
        currentIndex = 0;
        processNthElement(array);
    }

Then to process a large array, just call processArrayWithSetTimeout(array). However since we are using timeout, you need to process the result at the very last function call(see the ment in the function). If an array has 10000 elements, it will take more than 10000 milliseconds or 10 seconds to process, but the UI won't be freezed.

Note that this still processes the array sequentially but without freezing the UI as it waits for a while after processing 1 element.

Javascript is asingle thread engine. so large putation will temporary block all other event. You can use Web worker to run such big scripts in the background

You can use recursion, declare setTimeout as a variable to be able to "break" recursive call to function which calls setTimeout()

var curr, fn = (arr, i = -1, n = arr[++i]) => 
                 curr = setTimeout(() => {
                          // do stuf with `n`: current index in `arr` array
                          console.log(n);
                          // recursively call `fn` if condition met; e.g., 
                          // `i` less than `arr.length` 
                          i < arr.length ? fn(arr, i) : (curr = void 0)
                        }, 0);

// e.g.
fn(Array(10000).fill(1));
// clear timeout, stop recursion 
clearTimeout(curr);

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论