EventLoop——在我眼中的事件循环

2019-05-21  本文已影响0人  神话降临
第一次听说

第一次知道时间循环是年初面试的时候,当时为了面试找了一些面经,其中出现比较频繁的知识点就是,怎么理解EventLoop

万能的谷歌

然后就是在网上各种搜索文章,大多数文章都是千篇一律,对别人的文章抄抄改改没有注入自己的理解和思想,看得我也是云里雾里,当然肯定是有好文章的,我在这里推荐一篇大家可以去看下一次弄懂Event Loop(彻底解决此类面试问题)

关于链接的文章

我对于eventloop的知识体系也是通过这篇文章建立的,这篇文章分别是从浏览器和nodejs的方面来写的,由于我对nodejs了解的很少我们就来讨论一下浏览器的事件循环机制

这篇文章存在的理由

对于浏览器事件循环是如何运作的,这篇文章写得已经很清楚了,当然还是有一些细节需要来探讨一下,这就是这篇文章存在的理由

先总结一波

堆:堆是一种数据结构,是利用完全二叉树维护的一组数据
栈:栈在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。 栈是一种数据结构,它按照\color{red}{后进先出}的原则存储数据
队列: 允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入\color{red}{先进先出}

任务

javascript 中任务分为两种,宏任务和微任务
\color{red}{宏任务(MacroTask):}script全部代码,setTimeout,setInterval,I/O,UI rendering
\color{red}{微任务(MicroTask):}Process.nextTick(Node独有),promise,MutationObserver
其实我感觉这里是有点问题的 因为script的全部代码肯定是包含所有的宏任务和所有的微任务的,但是这里被归类为宏任务

JS调用栈

Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。

//关于后进先出举个例子
function add(a,b) {
  return a+b
}
    
function getResult(c) {
    let result = add(1,2) + c
    return result
 }

关于上面这个例子 当我执行getResult的时候调用栈会把getResult先读取到栈内,然后再把add函数读取到栈内,当add有了返回值之后,把add函数踢出栈,当getResult有了结果之后再把getResult踢出栈,看到了吧这就是后进先出的效果

同步任务和异步任务

Javascript单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在\color{red}{异步任务有了结果后},将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行
我为什么要把上面\color{red}{异步任务有了结果后}标红,我们先看一个例子

console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end') 
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')

输出结果在不同浏览器关于await的问题所以输出有一点不懂得地方
我的浏览器输出结果script start,async1 end,Promise,script end,async1 end,promise1,promise2,setTimeout
关于promise不懂得地方可以去看我之前的文章promise入门
只是比较正常的情况对吧,那我把上个例子稍微改一下

 import axios from 'axios'
    console.log('script start')

    setTimeout(function() {
        console.log('setTimeout')
    }, 1000)

    axios.get('http://www.douban.com').then(
        res =>{
            console.log('promise')
        }
    )
    console.log('script end')

我们都知道axios的底层是以promise封装的,不要在意我上面的接口是啥,我举这个例子就是想要说明如果我真的有接口的情况下,微任务promise.then()里面的内容还会比setTimeout 1s后先执行嘛???
这个肯定是不一定的对吧,因为这个要看我接口返回的速度,所以大家知道\color{red}{异步任务有了结果后}为什么要标红了吧,\color{red}{我们是在异步有了结果后才会把我们的回调函数注入微任务队列}

执行顺序

我们再来回顾一下上面两个例子事件循环的执行顺序
1.我们会先执行宏任务script
2.因为script里面有各种同步和异步,当我们遇到setTimeout的时候让他挂到浏览器上执行(js虽然是单线程,但是浏览器可是多线程的),当遇到promise或者axios像后台发起的请求我们就把这个任务交给浏览器的线程或者对应的后台或者数据库去处理,直到有了结果之后我们才会把这些对应的回调放到对应的宏任务或者微任务队列
3.当script全部执行一遍后,就会去看微任务队列是否有微任务,有的话执行完这个队列里的所有微任务,然后再去执行宏任务
4.宏任务执行完之后,就再去去看微任务队列是否有任务,有的话执行完这个队列里的所有微任务,然后再执行宏任务
就这样循环执行,直到所有的任务都被执行完

好了就写这么多吧,后续有新的感悟再往上加
如果对你有帮助就帮忙点个赞吧

上一篇下一篇

猜你喜欢

热点阅读