How to break up a long running function in javascript, but keep performance

Active3 hr before
Viewed126 times

7 Answers


I need another solution, one which has comparable performance to the loop but which does not cause a javascript execution warning. Unfortunately webWorkers cannot be used in this instance. ,For the most part this is fine but when I am dealing with arrays above around 1500 or so we get to the point of recieving a javascript execution alert message.,System will queue the timeout events for you and they will be called immediately one after another. And if users click anything during execution, they will not notice any lag. And no "script is running too long" thing.,I have a long running function. Which iterates through a large array and performs a function within each loop.

Here's some batching code modified from an earlier answer I had written:

var n = 0,
   max = data.length;
batch = 100;

(function nextBatch() {
   for (var i = 0; i < batch && n < max; ++i, ++n) {
   if (n < max) {
      setTimeout(nextBatch, 0);
load more v

You can use the Frame rate and Waterfall tools to see when JavaScript is causing performance problems, and to single out the particular functions that need attention.,it can be difficult to split up a long-running function into separate self-contained functions. Even this very simple case produced more complex code.,We can select one of these periods and have a closer look at it in the main Waterfall view:,In the first attempt at fixing this, we'll split up the function into a number of much smaller self-contained functions, and schedule each one using requestAnimationFrame().

const iterations = 50;
const multiplier = 1000000000;

function calculatePrimes(iterations, multiplier) {
   var primes = [];
   for (var i = 0; i < iterations; i++) {
      var candidate = i * (multiplier * Math.random());
      var isPrime = true;
      for (var c = 2; c <= Math.sqrt(candidate); ++c) {
         if (candidate % c === 0) {
            // not prime
            isPrime = false;
      if (isPrime) {
   return primes;

function doPointlessComputationsWithBlocking() {
   var primes = calculatePrimes(iterations, multiplier);
   pointlessComputationsButton.disabled = false;
load more v

Avoid the for-in loop unless you need to iterate over a number of unknown object properties.,The best ways to improve loop performance are to decrease the amount of work done per iteration and decrease the number of loop iterations.,The new loop code has two fewer operations per iteration, which can lead to increasing performance gains as the number of iterations increases.,Note that placing a var statement in the initialization part of a for loop creates a function-level variable, not a loop-level one. JavaScript has only function-level scope, and so defining a new variable inside of a for loop is the same as defining a new variable outside of the loop.

for (var i = 0; i < 10; i++) {
   //loop body
var i = 0;
while (i < 10) {
   //loop body
var i = 0;
do {
   //loop body
} while (i++ < 10);
for (var prop in object) {
   //loop body
load more v

So if you iterate 100 times and the timeout minimum is 1ms the timeout will add 100ms to the total time to complete the iteration.,The ratio of the iteration time to the timeout time needs to be controlled. If the function takes 1ms and the timeout adds 1ms you double the time to do the function. If however the function takes 100ms to do a single iteration then the timeout adds only 1% to the time to complete.,The await can be improved using a object to handle the promises based on time rather than iteration count.,You have to clutter your code with these instructions. Especially the more time granularity you would like to have, the more statements you have to insert.

The way you have implemented it can be improved

await new Promise(resolve => setTimeout(resolve, 0));

Inside the loop you use await as follows

await idler.idle();

The following is a general purpose interrupt to allow for pending events to be processed. Ill call it idler for want of a better name.

// idler.interval is min time between idle execution context in ms
// idler.start() sets the timer to now (not really needed for small values 
// of idler.interval. Should not be called inside a process
// idler.idle()  Request idle promise. Returns promise if time since last idle
// is greater than idler.interval. Else returns undefined.
export const idler = (() => {
   var lastIdle = 0;
   var interval = 3; // ms
   function timeout(ready) {
      setTimeout(ready, 0)
   return {
      idle() {
         var now =;
         if (now - lastIdle > interval) {
            lastIdle = now;
            return new Promise(timeout);
      start() {
         lastIdle =
      set interval(val) {
         interval = val >= 1 ? val : 1
      get interval() {
         return interval
#datalog {
   font - family: consola;
   font - size: 12 px;
   color: #0F0;
body {
   background : black;
<div id="datalog"></div>
load more v

The runtime profiler monitors the system being run and identifies “hot” functions (i.e. code that ends up spending a long time running)., The Profile tab gives you information about your code’s performance.,As you can see above, a perfomance.memory property is also available that gives access to JavaScript memory usage data such as the total heap size.,trace-deopt - log a list of code it had to deoptimize while running.

In quite a few discussions online about reclaiming memory in JavaScript, the delete keyword is brought up, as although it was supposed to be used for just removing keys from a map, some developers think you can force de-referencing using it. Avoid using delete if you can. In the below example, delete o.x does a lot more harm than good behind the scenes, as it changes o’s hidden class and makes it a generic slow object.

var o = {
   x: 1
delete o.x; // true
o.x; // undefined
load more v

Plenty of valuable information already exists about the Node.js Event-Loop, but it took me time find the answers for the specific questions I wanted to ask. I’ll do my best to share the various questions I had, and share the answers I’ve found in various great articles, some fun experimentation and digging.,and now let’s add a nasty event-loop blocking endpoint:,If an immediate timer is queued from inside an executing callback, that timer will not be triggered until the next event loop iteration,

So let’s start with a simple Express server:

const express = require('express');

const PID =;

function log(msg) {
   console.log(`[${PID}]`, new Date(), msg);

const app = express();

app.get('/healthcheck', function healthcheck(req, res) {
   log('they check my health');
   res.send('all good!\n')

const PORT = process.env.PORT || 1337;
let server = app.listen(PORT, () => log('server listening on :' + PORT));
load more v

There is a Garbage Collector (GC) in the browser which cleans memory occupied by unreachable objects; i.e., objects will be removed from memory if and only if the GC believes that they are unreachable. Unfortunately, it’s fairly easy to end up with defunct “zombie” objects that are in fact no longer in use but that the GC still thinks are “reachable”.,As JavaScript coding techniques and design patterns have become increasingly sophisticated over the years, there’s been a corresponding increase in the proliferation of self-referencing scopes within callbacks and closures, which are a fairly common source of “this/that confusion”.,The following objects are assumed to be reachable and are known as “roots”:,Memory Leak Example 1: Dangling references to defunct objects

Consider this example code snippet:

Game.prototype.restart = function() {
   this.timer = setTimeout(function() {
      this.clearBoard(); // what is "this"?
   }, 0);
load more v

Other "function-running" queries related to "How to break up a long running function in javascript, but keep performance"