前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript进阶之路系列(二): 事件循环

JavaScript进阶之路系列(二): 事件循环

作者头像
刘亦枫
发布2020-04-08 18:13:31
6190
发布2020-04-08 18:13:31
举报
文章被收录于专栏:亦枫的大佬之路

我们面试的时候经常会问到事件循环,也就是event loop。很多时候我们都是一脸懵,我们通常会背关于事件循环的面试题,讲给面试官的时候自己都不知道自己在讲什么,可能面试官也不太了解事件循环,只是看别人都这么问。那么,仔细了解一下事件循环吧,对以后的编程真的会有帮助的。

1.为什么js是单线程?

js作为主要运行在浏览器的脚本语言,js主要用途之一是操作DOM。

举一个例子,如果js同时有两个线程,同时对同一个dom进行操作,这时浏览器应该听哪个线程的,如何判断优先级?

为了避免这种问题,js必须是一门单线程语言,并且在未来这个特点也不会改变。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

代码语言:javascript
复制
var i, t = Date.now()
for (i = 0; i < 100000000; i++) {}
console.log(Date.now() - t) // 238

像上面这样,如果排队是因为计算量大,CPU忙不过来,但是,如果是网络请求就不合适。因为一个网络请求的资源什么时候返回是不可预知的,这种情况再排队等待就不明智了。

所以出现了同步与异步。

2.同步和异步

同步

同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。

代码语言:javascript
复制
// 同步代码
function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
fun1();
fun2();

输出会依次输入1,2,因为代码是从上到下依次执行,执行完fun1(),才继续执行fun2()。

异步

异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。

代码语言:javascript
复制
function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
function fun3() {
  console.log(3);
}
fun1();
setTimeout(function(){
  fun2();
},0);
fun3();

依次输出1,3,2,因为我们会优先执行同步函数,然后在执行异步函数。

正是由于JavaScript是单线程的,而异步容易实现非阻塞,所以在JavaScript中对于耗时的操作或者时间不确定的操作,使用异步就成了必然的选择。 JavaScript的执行顺序:(重点)

1.先同步后异步。 2.异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列。 3.调用Promise 中的resolve,reject属于微任务队列,setTimeout属于宏任务队列。

那什么是微任务和宏任务?

3.宏任务与微任务

异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。

宏任务

macrotask,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个macrotask执行结束后,在下一个macrotask 执行开始前,对页面进行重新渲染,流程如下:

代码语言:javascript
复制
macrotask->渲染->macrotask->...

宏任务包含:

script(整体代码) setTimeout setInterval I/O UI交互事件 postMessage MessageChannel setImmediate(Node.js 环境)

微任务

microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

微任务包含:

Promise.then Object.observe MutaionObserver process.nextTick(Node.js 环境)

4.Event Loop(事件循环)

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

1.执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行。 2.检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列。 3.更新render(每一次事件循环,浏览器都可能会去更新渲染)。 4.重复以上步骤。

宏任务 > 所有微任务 > 宏任务,如下图所示:

从代码执行顺序的角度来看,程序最开始是按代码顺序执行代码的,遇到同步任务,立刻执行;遇到异步任务,则只是调用异步函数发起异步请求。此时,异步任务开始执行异步操作,执行完成后到消息队列中排队。程序按照代码顺序执行完毕后,查询消息队列中是否有等待的消息。如果有,则按照次序从消息队列中把消息放到执行栈中执行。执行完毕后,再从消息队列中获取消息,再执行,不断重复。

由于主线程不断的重复获得消息、执行消息、再取消息、再执行。所以,这种机制被称为事件循环。 用代码表示:

代码语言:javascript
复制
while (queue.waitForMessage()) {
  queue.processNextMessage();
}

如果当前没有任何消息queue.waitForMessage 会等待同步消息到达。

5.实例

下面以一个实例来解释事件循环机制:

代码语言:javascript
复制
console.log(1)
div.onclick = () => {console.log('click')}
console.log(2)
setTimeout(() => {console.log('timeout')},1000)

1、执行第一行代码,第一行是一个同步任务,控制台显示1

2、执行第二行代码,第二行是一个异步任务,发起异步请求,可以在任意时刻执行鼠标点击的异步操作

3、执行第三行代码,第三行是一个同步任务,控制台显示2

4、执行第四行代码,第四行是一个异步任务,发起异步请求,1s后执行定时器任务

5、假设从执行第四行代码的1s内,执行了鼠标点击,则鼠标任务在消息队列中排到首位

6、从执行第四行代码1s后,定时器任务到消息队列中排到第二位

7、现在同步任务已经执行完毕,则从消息队列中按照次序把异步任务放到执行栈中执行

8、则控制台依次显示’click‘、‘timeout’

9、过了一段时间后,又执行了一次鼠标点击,由于消息队列中已经空了,则鼠标任务在消息队列中排到首位

10、同步任务执行完毕后,再从消息队列中按照次序把异步任务放到执行栈中执行

11、 则控制台显示’click’

异步过程

下面以一个实例来解释一次完整的异步过程:

代码语言:javascript
复制
div.onclick = function fn(){console.log('click')}

1、主线程通过调用异步函数div.onclick发起异步请求

2、在某一时刻,执行异步操作,即鼠标点击

3、接着,回调函数fn到消息队列中排队

4、主线程从消息队列中读取fn到执行栈中

5、然后在执行栈中执行fn里面的代码console.log(‘click’)

6、于是,控制台显示’click’

例题: 依次输出什么?

代码语言:javascript
复制
    function fun1() {
        console.log("1")
    }

    fun1()

    setTimeout(function () {
        console.log('2')
    });

    function fun3() {
        console.log("3")
    }
    fun3()
    var l4 = new Promise(function (resolve) {
        for (var i = 0; i < 10000; i++) {
            i == 99 && resolve();
        }
    }).then(function () {
        console.log('4')
    });

    console.log('5');
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.为什么js是单线程?
  • 2.同步和异步
  • 3.宏任务与微任务
  • 4.Event Loop(事件循环)
  • 5.实例
  • 例题: 依次输出什么?
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档