Out of all the questions I have seen on this site about setTimeout the majority of them people are executing the method in a setTimeout declaration. I am not doing this.
Code:
var nums = [1,2,3,4,5,6,7,8,9];
for(var i=0; i < nums.length; i++){
setTimeout(function(num){
var url = getUrl(num);
//query(url); // prints out query operation and data return
console.log(num);
}, 1000, nums[i]);
}
When running the command to start the app, query will execute all items in the array no matter how large I set the millisecond delay. When running this code with printing out to console I get the same effect. Am I doing this wrong? Can anyone else duplicate this?
I created an app that would run setInterval to test api throttling, and it works as expected. I am thinking I may be missing some kind of backend knowledge about node with setTimeout and loops? setTimeout Docs
The docs do say that it won't guarantee the time that it will execute the callback, but I have 120 items that it is going to fire logging on and it fires them all at once.
since those function setTimeout or setInterval works asynchronously,
You have to call the next loop in the setTimeout/setInterval callback function.
var _i = 0
var doQuery = setInterval(function(){
if(_i == nums.length) clearInterval(doQuery);
var url = getUrl(nums[_i]);
query(url);
_i++;
},1000);
Related
My question is variant of this question: setTimeout executes after for loop in javascript
The code:
let i = 0;
setTimeout(() => alert(i), 100); // ?
// assume that the time to execute this function is >100ms
for(let j = 0; j < 100000000; j++) {
i++;
}
The output:
100000000
My Question:
I know that when we're using setTimeout with delay of 100 the WEB API setTimeout() will move to the queue of the eventloop and after that will get inside the call stack if it's empty and available.
We can see that this setTimeout will start after 100ms so i thought maybe the i value would be somthing like 4000+ or somthing like that.
but unfortunately (with this big loop that take like 5+- seconds that is 5000ms) the setTimeout function will be executed just after the for loop finishes! but the event loop daemon is running and ready to accept the setTimeout callback because the call stack is empty.
I know that JS concurrency concept is diffrent from languages just like JAVA&C++, etc... that there is no scheduler for the CPU time because it's one threaded language, but I guess that the event loop working like OS daemon and ready to accept the message from the queue even if the CPU is busy with the for loop.
so the reason that it's happening is because the CPU is busy and can't execute the eventloop ? or there is other reason?
Thanks guys!
I know that this might be a stupid question but it drives me crazy. I'm trying to change the innerHTML of a DOM element but it doesn't change until the end of the function's execution. For example:
function test(){
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; i++)
{
testEl.innerHTML = 'Count: ' + i;
alert(i);
}
}
Even if I have put an alert in the loop, the text of the element will not change until the end of the function's execution. How can the change be applied instantly (for example I mean during the loop)?
You can update the number every period of time using setInterval:
function test(){
let testEl = document.getElementById('testEl');
let i = 0;
const interval = setInterval(function(){
testEl.innerHTML = `Count: ${i++}`;
if(i === 5)
clearInterval(interval);
}, 1000);
}
test();
<p id="testEl"></p>
JavaScript runs in a single-threaded environment. This means that only one execution context can ever be running at any single point in time. Asynchronous code executes outside of the JavaScript runtime environment (in this case by the browser's native processing) and only when the JavaScript thread is idle can the results of an asynchronous request be executed (i.e. callbacks).
Below is an example that updates a DOM element approximately every second, creating a clock. However, if you click the button, it will ask the browser to render an alert, which is handled outside of the JavaScript runtime and is a blocking UI element, so the clock will stop. Once you clear the alert, you will see the time jump to be roughly current.
As you'll see, the asynchronous API call to window.setInterval() allows for the function to run repeatedly, every so often, and therefore not continuously. This replaces the need for a loop that runs in its entirety every time its accessed. Because of this, you can see updates to the webpage instead of the last value of your loop.
See the comments for more details:
const clock = document.querySelector("span");
// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
// Update the DOM
clock.textContent = new Date().toLocaleTimeString();
}, 900);
document.querySelector("button").addEventListener("click", function(){
// An alert is also not JavaScript, but another browser API that is executed
// by the browser, not JavaScript. However, it is a blocking (modal) UI element.
// The rest of the browser interface (including the web page) cannot update
// while the alert is present. As soon as the alert is cleared, the UI will update.
window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>
Another way to achieve it is by using async - Promise like this
async function test() {
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; ++i) {
testEl.innerHTML = 'Count: ' + i;
await new Promise((resolve, _) => {
setTimeout(function() {
resolve("");
}, 1000 /* your preferred delay here */);
});
}
}
In the next code, I want to process several files at the same time without wait to the end of each other. For this reason, I first read the files (array) and then the callback is called to process an element of this array instance.
I have found a problem into this javascript code, exactly in a async for-loop, where this process is executed as a sync code instead of async.
var array = ['string1','string2','string3','string4'];
function processArray (arrayString,callback){
//Read file Example.csv thought sync way
try{
var ifs = new InputFileStream('Example.csv','utf8');
table = ifs.read(0);
ifs.close();
}catch(err){
console.log(err.stack);
}
callback(arrayString, table);
}
//Async for
for (var i=0; i<array.length; i++) {
processArray(array[i], function(arrayString, table){
//Here process the file values thought async way
console.log('processed_'+i);
});
}
You could put the call back in a setTimeout with a delay of 1ms. That will run it in the next block of execution and your loop will continue on.
e.g. use this:
setTimeout(function() { callback(arrayString, table); }, 1);
instead of this:
callback(arrayString, table);
An alternative to this is to run the callback on a separate thread using Web Workers. I don't think it would appropiate to provide a long answer describing how to do multi threaded JavaScript here so I'll just leave the link to the docs. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
where this process is executed as a sync code instead of async
I've seen that you just have find out the answers of your question, remember that JavaScript is single thread.
So, for that when you execute operations that require full use of CPU like for..loops, while, etc; you just will get your code running synchronous and not only that,
You will get your web page freeze if they are huge loops
Let me give you an example, this is a while loop that will run for 6 seconds, look how you cannot do anything in stackoverflow.
function blocker (ms) {
console.log('You cannot do anything')
var now = new Date().getTime();
while(true) {
if (new Date().getTime() > now +ms)
return;
}
}
blocker(6000) //This stop your entire web page for 6 seconds
If you really want to achieve running blocking code in the background read about Web Workers or you just can use a small library I wrote, that allow you to execute a blocking CPU function in the background, I called it GenericWebWorker
While waiting for the back end devs to implement a "cancel all" feature, which cancels all tasks tracked by the back end, I am attempting to makeshift it by cancelling each individual task. The cancel REST service accepts an ID in the form of a data object {transferID: someID}.
I use a FOR loop to iterate over an array of IDs that I have stored elsewhere. Anticipating that people MAY end up with dozens or hundreds of tasks, I wanted to implement a small delay that will theoretically not overflow the number of HTTP requests the browser can handle and will also reduce a blast of load on the back end CPU. Here is some code with comments for the purpose of this discussion:
ta.api.cancel = function (taskArray, successCallback, errorCallback) {
// taskArray is ["task1","task2"]
// this is just the latest attempt. I had an attempt where I didn't bother
// with this and the results were the same. I THOUGHT there was a "back image"
// type issue so I tried to instantiate $.ajax into two different variables.
// It is not a back image issue, though, but one to do with setTimeout.
ta.xhrObjs = ta.xhrObjs || {};
for (var i = 0; i < taskArray.length; i++) {
console.log(taskArray); // confirm that both task1 and task2 are there.
var theID = taskArray[i];
var id = {transferID: theID}; // convert to the format understood by REST
console.log(id); // I see "task1" and then "task2" consecutively... odd,
// because I expect to see the "inside the setTimeout" logging line next
setTimeout(function () {
console.log('inside the setTimeout, my id is: ')
console.log(id.transferID);
// "inside the setTimeout, my id is: task2" twice consecutively! Y NO task1?
ta.xhrObjs[theID] = doCancel(id);
}, 20 * i);
}
function doCancel(id) {
// a $.Ajax call for "task2" twice, instead of "task1" then "task2" 20ms
// later. No point debugging the Ajax (though for the record, cache is
// false!) because the problem is already seen in the 'setTimeout' and
// fixed by not setting a timeout.
}
}
Thing is: I know setTimeout makes the containing function execute asynchronously. If I take out the timeout, and just call doCancel in the iterator, it will call it on task1 and then task2. But although it makes the call async, I don't understand why it just does task2 twice. Can't wrap my head around it.
I am looking for a way to get the iterator to make the Ajax calls with a 20ms delay. But I need it to call on both! Anybody see a glaring error that I can fix, or know of a technique?
You must wrap your function setTimeout and pass the id variable into it, like this:
(function(myId, i) {
setTimeout(function () {
console.log('inside the setTimeout, my id is: ', myId);
}, 20 * i);
}(theId, i));
This pattern does not create a unique variable1 for each instance of the loop as one might expect.
function () {
for (var i = 0; i < length; i++) {
var variable1;
}
}
In javascript variables are "hoisted". To quote Mozilla:
"Because variable declarations (and declarations in general) are
processed before any code is executed, declaring a variable anywhere
in the code is equivalent to declaring it at the top."
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
So it should be re-written as:
function () {
var variable1;
for (var i = 0; i < length; i++) {
}
}
What this means is that after the loop has finished, any asynchronous callbacks that reference this variable will see the last value of the loop.
I have to use atleast 2 setTimeouts and 1 setInterval. Does this have any dependency on the browser or javascript engine being used?
tl;dr: Don't worry about the cost of timers until you're creating 100K's of them.
I just did a quick test of timer performance by creating this test file (creates 100K timers over and over):
<script>
var n = 0; // Counter used to verify all timers fire
function makeTimers() {
var start = Date.now();
for (var i = 0; i < 100000; i++, n++) {
setTimeout(hello, 5000);
}
console.log('Timers made in', Date.now() - start, 'msecs');
}
function hello() {
if (--n == 0) {
console.log('All timers fired');
makeTimers(); // Do it again!
}
}
setTimeout(makeTimers, 10000); // Wait a bit before starting test
</script>
I opened this file in Google Chrome (v54) on my circa ~2014 Macbook Pro, and went to the Timeline tab in Developer Tools and recorded the memory profile as the page loaded and ran thru 3-4 cycles of the test.
Observations
The timer creation loop takes 200ms. The page heap size starts at 3.5MB pre-test, and levels out at 3.9MB.
Conclusion
Each timer takes ~.002 msecs to set up, and adds about 35 bytes to the JS heap.
On a page you can have as many setTimeouts/setIntervals running at once as you wish, however in order to control each individually you will need to assign them to a variable.
var interval_1 = setInterval("callFunc1();",2000);
var interval_2 = setInterval("callFunc2();",1000);
clearInterval(interval_1);
The same code above applies to setTimeout, simply replacing the wording.
As Kevin has stated, JavaScript is indeed single threaded, so while you can have multiple timers ticking at once, only one can fire at any one time - i.e. if you have one that fires a function which 'halts' in execution, for example with an alert box, then that JS must be 'resumed' before another can trigger I believe.
One further example is given below. While the markup is not valid, it shows how timeouts work.
<html>
<body>
<script type="text/javascript">
function addThing(){
var newEle = document.createElement("div");
newEle.innerHTML = "Timer1 Tick";
document.body.appendChild(newEle);
}
var t1= setInterval("addThing();",1000);
var t2 = setInterval("alert('moo');",2000);
</script>
</body>
</html>
You can use as many as you want. Just remember that JavaScript is single threaded, so none of them can execute in parallel.
var interval_1 = setInterval("callFunc1();",2000); calls eval() which is evil so it's BAD.
Use this instead var interval_1 = setInterval(callFunc1,2000);
And for the question, you may use as many as you want but if all have the same interval between two actions, you better do it this way
var interval = setInterval(function() {
// function1
fct1();
// function2
fct2();
},2000);