Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update element with ajax don't affect until for loop end

I want to print a lot of numbers ONE-BY-ONE with AJAX.

something like this: (each new line is update of previous line!)

output is:
    1
    12
    123
    1234
    12345
    123456
    ...

I tried a lot and read a lot of this same problem, but i couldn't find my right Answer.

The real problem is every FOR LOOP in javascript will NO affect the DOM after it will END the loop. I just want update the DOM inside the FOR LOOP while working on a long running job.

Please look at my code.

$("#btn").on("click", dowork);

function dowork() {
  document.getElementById("foo").innerHTML = "working";
  setTimeout(function() {
    var counter = 100; // i want assign counter = 2000000000
    for (var i = 0; i < counter; i++) {
      document.getElementById("print_here").innerHTML += i;
    }
    document.getElementById("foo").innerHTML = "done!";
  }, 50);
}
#btn {
  background: #1f1f1f;
  padding: 10px;
  font-weight: bolder;
  color: #fff;
  width: 50%;
  margin: auto;
  margin-top: 10px;
  text-align: center;
  cursor: pointer;
}

#print_here {
  overflow-wrap: break-word;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="btn">CLICK TO DO WORK</div>
<div id="foo"></div>
<div id="print_here"></div>

Thanks for any answer and help to solve this problem.

like image 982
NGM Avatar asked Jan 10 '18 09:01

NGM


People also ask

How do I make jQuery wait for an AJAX call to finish before it returns?

If you don't want the $. ajax() function to return immediately, set the async option to false : $(". my_link").

Does jQuery return AJAX promise?

jQuery have promises implemented with their AJAX methods. In a nutshell, they are utilities that allow us to work with events that have completed or put them in queues or chain them – all of that good stuff. In our case, we need a “promise“. This allows us to interact with our AJAX requests – well outside our $.

How do you check if all AJAX calls are completed?

jQuery ajaxStop() Method The ajaxStop() method specifies a function to run when ALL AJAX requests have completed. When an AJAX request completes, jQuery checks if there are any more AJAX requests. The function specified with the ajaxStop() method will run if no other requests are pending.

How do I know if AJAX is working?

Try the following steps: Open developer console by pressing CTRL + SHIFT + I and go to Network tab. Click on XHR tab( uncheck all other tab to see only ajax) and select URL you want to check. Then You can inspect Header, Cookies, Response etc from respective tab.


2 Answers

Your DOM is "locked" while it is being updated and redrawn ones the loop is done. You can free up the resource to let the DOM update each time wrapping your DOM change in a setTimeout, similar to:

setTimeout(function(){
    document.getElementById("print_here").innerHTML += i;
},1);

To ensure setTimeout uses the correct value for i use let i instead of var i

$("#btn").on("click", dowork);

function dowork() {
  document.getElementById("foo").innerHTML = "working";

  var counter = 3000; // i want assign counter = 2000000000
  for (let i = 0; i < counter; i++) {
    setTimeout(function() {
      document.getElementById("print_here").innerHTML += i;
    }, 1);
  }
  document.getElementById("foo").innerHTML = "done!";
}
#btn {
  background: #1f1f1f;
  padding: 10px;
  font-weight: bolder;
  color: #fff;
  width: 50%;
  margin: auto;
  margin-top: 10px;
  text-align: center;
  cursor: pointer;
}

#print_here {
  overflow-wrap: break-word;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="btn">CLICK TO DO WORK</div>
<div id="foo"></div>
<div id="print_here"></div>

I want change the #foo into "done!" after the FOR statement is END

You could check if you are at your last item you process within the setTimeout, similar to:

if (i == counter - 1){
    document.getElementById("foo").innerHTML = "done!";
}

$("#btn").on("click", dowork);

function dowork() {
  document.getElementById("foo").innerHTML = "working";

  var counter = 3000; // i want assign counter = 2000000000
  for (let i = 0; i < counter; i++) {
    setTimeout(function() {
      document.getElementById("print_here").innerHTML += i;
      if (i == counter - 1){
        document.getElementById("foo").innerHTML = "done!";
      }
    }, 1);
  }
}
#btn {
  background: #1f1f1f;
  padding: 10px;
  font-weight: bolder;
  color: #fff;
  width: 50%;
  margin: auto;
  margin-top: 10px;
  text-align: center;
  cursor: pointer;
}

#print_here {
  overflow-wrap: break-word;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="btn">CLICK TO DO WORK</div>
<div id="foo"></div>
<div id="print_here"></div>
like image 142
Nope Avatar answered Sep 30 '22 05:09

Nope


You need to let the call stack complete so the browser can do its work on the page. If you bog down the one main thread, page updates aren't going to occur.

One way to do this is use setImmediate or nextTick. This is non-standard, so check this polyfill: https://www.npmjs.com/package/browser-next-tick

Basically, you do an iteration, then tell the browser to do the next iteration as soon as possible... and this occurs on a fresh call stack.

like image 23
Brad Avatar answered Sep 30 '22 05:09

Brad