参考:
阮一峰event loop
MDN Concurrency model and Event Loop
阮一峰线程和进程概念
javascript Event Loop机制详解
线程和进程
进程是指CPU所能处理的单个任务,任一时刻,CPU总是运行一个进程,而一个进程里可以有几个线程,几个线程同时进行,协同完成一个任务。
什么是event loop??
事件循环
简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为”主线程”;另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为”Event Loop线程”(可以译为”消息线程”)
像javascript就是基于单线程的运行机制,这种模型与其他语言如c,java就有非常明显的区别。
任务队列
javascript的单线程就意味着所有的任务都需要排队,前一个执行完后一个才会执行。
image.png这样看来,其实等待的时间甚至有时候会多于执行的时间,造成cpu空等并且后面的事项又很紧急等待的情况。
这时,javascript语言的设计者就了解到,可以先挂起正在等待返回的任务,先执行后面的任务
于是,所有任务就可以分为两种,同步任务和异步任务。同步任务是指主线程上的任务,只有前一个任务执行完毕才能执行后一个任务;异步任务是指不进入主线程,进入’任务队列‘的任务。
任务队列中的事件
image.png像这样的机制通常运用在一些不是可以立即执行的,比如用户产生的时间,只要有指定回调函数,这些事件就会进入“任务队列”,等待主线程完成后进行读取。
任务队列也是先进先出的数据结构。
异步任务的事件
除此之外,只要是有回调函数的事件,就几乎都是异步事件,会被放进任务队列中。拿setTimeout()事件来说,这是最经典的事件了。
setTimeout()接收两个参数,第一个是回调函数,第二个是延迟时间
然而,在下面的例子中你会发现,即使延迟时间为0,还是相同的结果,这就是因为任务队列中的事件需要在主线程完成之后再执行,通过这样我们也可以看到,如果主线程任务很多,定时器的延迟时间比较短,就会出现不准的情况。
var m = 1;
var n = 2;
setTimeout(function() {
console.log(m);
}, 0);
console.log(n);
//2
//1
小试题
在一篇帖子看到的,刚开始还想不明白ヽ(≧□≦)ノ。
摘录下来。
function foo() {
console.log('1');
bar();
setTimeout(function() {
console.log('2');
}, 0);
Promise.resolve().then(function() {
console.log('3');
Promise.resolve().then(function() {
console.log('4');
});
});
console.log('5');
}
function bar() {
setTimeout(function() {
console.log('6');
}, 0);
setTimeout(function() {
console.log('7');
}, 0);
}
foo();
哈哈哈公布答案
image.pngQ:刚开始不明白的是为什么2是排在6和7的后面,明明bar()里面的setTimeout比直接setTimeout多一个调用级别?
后来想到,在一次调用bar()后,进入bar()函数里面,就要把该函数里的进程运行结束才会返回,所以bar()函数里面的setTimeout要运行结束才会出来继续执行任务队列里接下来的其他任务。
-
2 一个for循环添加事件
function send(argument) { console.log(argument); } var m = document.getElementById('ok'); for (var i = 0; i < 5; i++) { console.log(i); //0,1,2,3,4 m.addEventListener('click', function () { console.log(i) //5,5,5,5,5 }) }
这里看到不一样的输出结果,这有两方面的原因
1 是因为var声明的变量存在变量提升的问题
2 是因为js单线程所以动态添加事件的事件被挂起,直到主线程结束
解决的办法很简单,把var改为let,就不存在变量提升的问题。或者也可以从事件挂起的角度出发,找其他的解决办法如闭包
网友评论