JavaScript single thread
JavaScript's single threading is related to its purpose. As a browser scripting language, JavaScript's main purpose is to interact with users and operate DOM. This determines that it can only be single threaded, otherwise it will cause very complex synchronization problems. For example, suppose JavaScript has two threads at the same time, one thread adds content on a certain DOM node, and the other thread deletes this node, which thread should the browser take at this time? Therefore, in order to avoid complexity, JavaScript is a single thread from its birth, which has become the core feature of this language and will not change in the future.
Queue Tasks
Single threading means that all tasks need to be queued, and the previous task will be executed before the next task will be executed. If the previous task takes a long time, the next task has to wait.
Asynchronous event driver
Many behaviors in the browser are asynchronized, such as: mouse click event, window size drag event, timer trigger event, XMLHttpRequest completion callback, etc. When an asynchronous event occurs, it enters the event queue. The browser has an internal large message loop, Event Loop, which will poll large event queues and process events. For example, the browser is currently busy processing the onclick event, and then another event occurs (such as window onSize), and this asynchronous event is put into the event queue and waits for processing. This event will be executed only when the previous processing is completed and is free.
Event Loop
JavaScript is single-threaded, but the browser is not single-threaded
The browser will have at least some of the following processes
1. Browser GUI rendering thread
2. JavaScript engine thread
3. Browser timed trigger thread
4. Browser event triggers thread
5. Browser http asynchronous request thread
Because the JavaScript engine is single-threaded, the code is first pressed to the queue and then run by the engine in a first-in-first-out manner. Event handling functions and timer execution functions will also be placed in this queue, and then use an infinite loop to continuously extract the functions from the head of the team to execute. This is Event Loop.
In summary, js is single-threaded, but the browser is multi-threaded. When encountering asynchronous things, the browser will put the asynchronous callback into the Event Loop. When the js thread is not busy, read the Event Loop.
Timer principle
How to use the timer
setTimeout(fn, delay)
setInterval(fn, delay)
fn is a function or a string, delay is the time of delay, unit is milliseconds
There are the following things to note
1. Although fn can be a string, it is never recommended to use it like this.
2. If there is this function in fn, this will point to the window when executing.
If you understand js single thread and Event loop well, the principle of timer will be easy to understand.
If a timer is set, when the delay time is reached, the browser will put the delayed execution event into the Event loop. When the time is up, if the js thread is idle, it will be executed (so the timer's accuracy is inaccurate)
I read an article about the difference between setTimeout and setInterval that always poll functions. The code is as follows
The code copy is as follows:
setTimeout(function(){
setTimeout(arguments.callee,100)
},100)
setInterval(function(){},1000)
The general meaning of the article is that setTimeout starts the next timer after the callback function is executed, so it must be executed at intervals, while setInterval is executed all the time. If you encounter a js thread that keeps executing, you may add multiple callbacks in the Event loop. When the js thread is not busy, multiple executions will be executed one after another.
After testing, it was found that setInterval is at a certain interval, no matter under ie, ff, chrome, Opera, and Safari.
The test code is as follows
The code copy is as follows:
setInterval(function(){
xx.innerHTML=xx.innerHTML+1;
},100);
for(var i=0;i<6000000;i++){
xx.offsetWidth
}
setTimeout(function(){
debugger;
},10)
When breakpoints are still printed only 1
Timer accuracy issues
Because of JS single thread, if you encounter busyness, the timer will definitely be inaccurate, and it will definitely be longer and longer. This seems to be unsolvable, no solution.
Another accuracy problem is the minimum interval setTimeout(fun,0)
When the js thread is not busy, it is impossible to execute immediately after 0 seconds. There is always a minimum interval, and each browser is still different. This has not been tested.
I read an article about the standard of w3c. The minimum time execution of the timer is 4ms. There is no way to verify the source if you can't find it! ! !
Some optimizations related to timers
There are still some optimizations when making timers
1. For example, if you bind window.onresize, the trigger will be very frequent when the browser is zoomed, so you can delay execution. When the next execution is cleared, it will reduce frequent execution.
The pseudo-code is as follows
The code copy is as follows:
var timer;
function r(){
clearTimeout(timer);
timer = setTimeout(function(){
//do something
},150);
}
2. When the scroll bar is pulled down, it is also a bit. For example, the lazyload of the picture should also have a timer to avoid excessive calculations.
3. When there are multiple places where timers are needed, you can merge them into a timer. The time interval is the smallest one. Then the callback function that needs to be executed is stuffed into the array. When the time interval is reached, iterate through the array to execute.
A small demo
The code copy is as follows:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
.wrap{width:80%; margin: 30px auto; border: 1px solid #ccc; padding: 20px;}
.c{border: 1px solid #ccc; height: 30px;margin-bottom: 20px;}
</style>
</head>
<body>
<div id="xx"></div>
<div >
<div id="a1">0</div>
<div id="a2">0</div>
<div id="a3">0</div>
<div id="a4">0</div>
</div>
<script src="http://static.paipaiimg.com/paipai_h5/js/ttj/zepto.min.js"></script>
<script type="text/javascript">
var runTime = {
options: {
step : 1000
},
callbacks:[],
addCallbacks : [],
start : false,
timer: null,
extend : function(){
var target = arguments[0] || {}, i = 1, length = arguments.length, options;
if ( typeof target != "object" && typeof target != "function" )
target = {};
for ( ; i < length; i++ )
if ( (options = arguments[ i ]) != null )
for ( var name in options ) {
var copy = options[ name ];
if ( target === copy )
continue;
if ( copy !== undefined )
target[ name ] = copy;
}
return target;
},
init : function(options){
$.extend(this,this.options,options||{});
},
add : function(fun,options){
options = options ||{};
this.addCallbacks.push({
fun : fun,
startTime: new Date().getTime(),
step : options.step || this.step,
i : 1
});
var self = this;
if(!this.start){
this.callbacks = [fun];
this.start = true;
this.startTime = new Date().getTime();
this.timer = setInterval(function(){
self.done();
},this.step);
}
},
done : function(){
var callbacks = this.callbacks,
self = this,
newArr = [];
$.each(callbacks,function(i,obj){
if(obj.step == self.step){
obj.fun();
}else{
if(obj.i == obj.step/self.step){
if((new Date().getTime())-obj.startTime>obj.step*2/3){
obj.fun();
}
obj.i = 1;
}else{
obj.i = obj.i + 1;
}
}
});
$.each(this.addCallbacks,function(i,obj){
if(obj.step == self.step){
if((new Date().getTime())-obj.startTime>obj.step*2/3){
obj.fun();
callbacks.push(obj);
}else{
newArr.push(obj);
}
}else{
obj.i = obj.i + 1;
callbacks.push(obj);
}
});
this.addCallbacks = newArr;
},
clear : function(){
clearInterval(this.timer);
}
}
runTime.init();
runTime.add(function(){
a1.innerHTML = ~~a1.innerHTML+1;
});
runTime.add(function(){
a2.innerHTML = ~~a2.innerHTML+1;
},{step:2000});
runTime.add(function(){
a3.innerHTML = ~~a3.innerHTML+1;
},{step:4000});
runTime.add(function(){
a4.innerHTML = ~~a4.innerHTML+1;
},{step:8000});
</script>
</body>
</html>
Have you any idea about the javascript timer? Leave me a message if you have any questions.