以前有看过一些事件循环的博客,不过一阵子没看就发现本身忘光了,因此决定来本身写一个博客总结下!html
就咱们所知,浏览器的js是单线程的,也就是说,在同一时刻,最多也只有一个代码段在执行,但是浏览器又能很好的处理异步请求,那么究竟是为何呢?咱们先来看一张图(这张图来自于http://www.zcfy.cc/article/node-js-at-scale-understanding-the-node-js-event-loop-risingstack-1652.html)
从上图咱们能够看出,js主线程它是有一个执行栈的,全部的js代码都会在执行栈里运行。在执行代码过程当中,若是遇到一些异步代码(好比setTimeout,ajax,promise.then以及用户点击等操做),那么浏览器就会将这些代码放到一个线程(在这里咱们叫作幕后线程)中去等待,不阻塞主线程的执行,主线程继续执行栈中剩余的代码,当幕后线程(background thread)里的代码准备好了(好比setTimeout时间到了,ajax请求获得响应),该线程就会将它的回调函数放到任务队列中等待执行。而当主线程执行完栈中的全部代码后,它就会检查任务队列是否有任务要执行,若是有任务要执行的话,那么就将该任务放到执行栈中执行。若是当前任务队列为空的话,它就会一直循环等待任务到来。所以,这叫作事件循环。node
其实(正如上图所示),js是有两个任务队列的,一个叫作Macrotask Queue(Task Queue),一个叫作Microtask Queuegit
那么,二者有什么具体的区别呢?或者说,若是两种任务同时出现的话,应该选择哪个呢?
其实事件循环作的事情以下:github
其中,在执行代码过程当中新增的microtask任务会在当前事件循环周期内执行,而新增的macrotask任务只能等到下一个事件循环才能执行了(一个事件循环只执行一个macrotask)
首先,咱们先来看一段代码ajax
console.log(1) setTimeout(function() { //settimeout1 console.log(2) }, 0); const intervalId = setInterval(function() { //setinterval1 console.log(3) }, 0) setTimeout(function() { //settimeout2 console.log(10) new Promise(function(resolve) { //promise1 console.log(11) resolve() }) .then(function() { console.log(12) }) .then(function() { console.log(13) clearInterval(intervalId) }) }, 0); //promise2 Promise.resolve() .then(function() { console.log(7) }) .then(function() { console.log(8) }) console.log(9)
你以为结果应该是什么呢?
我在node环境和chrome控制台输出的结果以下:chrome
1 9 7 8 2 3 10 11 12 13
在上面的例子中
第一次事件循环:promise
第二次事件循环:浏览器
第三次事件循环:异步
第四次事件循环:函数
在这里,你们能够会想,在第一次循环中,为何不是macrotask先执行?由于按照流程的话,不该该是先检查macrotask队列是否为空,再检查microtask队列吗?
缘由:由于一开始js主线程中跑的任务就是macrotask任务,而根据事件循环的流程,一次事件循环只会执行一个macrotask任务,所以,执行完主线程的代码后,它就去从microtask队列里取队首任务来执行。
因为在执行microtask任务的时候,只有当microtask队列为空的时候,它才会进入下一个事件循环,所以,若是它源源不断地产生新的microtask任务,就会致使主线程一直在执行microtask任务,而没有办法执行macrotask任务,这样咱们就没法进行UI渲染/IO操做/ajax请求了,所以,咱们应该避免这种状况发生。在nodejs里的process.nexttick里,就能够设置最大的调用次数,以此来防止阻塞主线程。
以此,咱们来引入一个新的问题,定时器的问题。定时器是不是真实可靠的呢?好比我执行一个命令:setTimeout(task, 100),他是否就能准确的在100毫秒后执行呢?其实根据以上的讨论,咱们就能够得知,这是不可能的。
缘由我想你们应该也都知道了,由于你执行setTimeout(task,100)后,其实只是确保这个任务,会在100毫秒后进入macrotask队列,但并不意味着他能马上运行,可能当前主线程正在进行一个耗时的操做,也可能目前microtask队列有不少个任务,因此这也多是你们一直诟病setTimeout的缘由吧哈哈哈哈
以上,只是我我的对事件循环的一些见解, 以及借鉴了其余优秀文章
参考:
http://www.zcfy.cc/article/node-js-at-scale-understanding-the-node-js-event-loop-risingstack-1652.html
https://github.com/ccforward/cc/issues/47