1. EventLoop 概念

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

因为JavaScript就是单线程,也就是说,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

2.同步任务和异步任务

在讲EventLoop之前,先说一下什么是 同步任务异步任务

同步任务:函数返回—>拿到预期结果
异步任务:函数返回—>拿不到预期结果,还要通过一定的手段获得结果

简单点说 :
同步任务:会立即执行的任务
异步任务:不会立即执行的任务(异步任务又分为宏任务与微任务)

在异步任务中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)

  • 宏任务:由宿主对象发起的任务(setTimeout)

    宏任务包括 scriptsetTimeoutsetIntervalsetImmediate(Node.js)I/OpostMessageMessageChannelUI rendering

  • 微任务:由js引擎发起的任务(promise)

    微任务包括 process.nextTick(Node.js)promise.then()promise.catch()MutationObserver

宏任务与微任务的概念不必强记,理解不了可先记住宏任务和微任务包含哪些情况。

由上述可知,在代码执行的过程中,同步任务会立即执行,异步任务会通过一些手段和过程才会拿到结果。所以在异步任务等待结果的同时,可先执行其后的同步任务。当异步任务有结果的时候,在回过头来执行异步任务。

所以 EventLoop 的执行机制如下:

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

话不多说,直接上题

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')

这道题有些繁琐,先放置一下,后面将会对这道题进行讲解。

EvnentLoop执行的机制就是先执行同步代码,接着是微任务,然后是宏任务

3. EventLoop 执行解析

遇到 Promise
在解答上题之前,先来两道简单点的,小试牛刀一下:
例题 1:

setTimeout(() => {console.log('A');
}, 0);
var obj = {func: function() {setTimeout(function() {console.log('B');}, 0);return new Promise(function(resolve) {console.log('C');resolve();});},
};
obj.func().then(function() {console.log('D');
});
console.log('E');

根据事件循环机制的执行顺序,上述代码执行步骤如下:

  • 执行 setTimeout ,由于是宏任务,将其放置进宏任务队列,此时宏任务队列为 [ 'A ’ ]

  • 接着执行 obj.func() , 先执行 setTimeout,由于是宏任务,将其放置进宏任务队列,此时宏任务队列为 [ 'A ','B ’ ]

  • 然后函数返回值是一个 Promise,因为这是一个同步操作,所以先打印出 'C ’

  • 紧接着是 promise.then() , 由于是一个微任务,将其放置进微任务队列,此时微任务队列为 [ 'D ’ ]

  • 接着就会执行同步任务,打印出 'E ’

  • 虽然当前微任务队列与宏任务队列均存在数据,但是微任务永远比宏任务先执行(换句话说 先清掉现存的微任务之后,才会执行宏任务队列),所以先打印 'D ’

  • 然后会执行宏任务,依次打印出来 'A ’ 和 'B ’

所以,这道题的打印顺序就是

C -> E -> D -> A -> B

要注意的是 obj.func().then() 这里,obj.func() 是普通函数/同步代码,后面的 .then() 才是微任务。

例题2:

function go() {console.log(5)
}
let p = new Promise(resolve => {resolve(1);Promise.resolve(go()).then(() => console.log(2));console.log(4);
}).then(t => console.log(t));
console.log(3);

上述代码执行步骤如下:

  • 在 new Promise的时候,就会执行内部的方法。首先就是执行 resolve(1) ,将 1 返回到 then 里
  • 紧接着会执行内部的 Promise.resolve( go() ) ,因为这是一个普通函数,是一个同步执行的代码,所以会打印出 5
  • promise.then()是一个微任务,会将 console.log(2) 放置到微任务队列,此时微任务队列为 [ 2 ]
  • 然后会顺序执行同步任务 console.log(4),将 4打印出来
  • 然后会执行 new Promise 的 then() 方法,由于这也是一个微任务,所以会放置到微任务列表,此时微任务队列为 [ 2 ,1 ]
  • 顺序执行同步任务 console.log(3),将 3打印出来
  • 最后,依次打印出微任务队列的 2 和 1

so,上述代码的打印结果为

5 -> 4 -> 3 -> 2 -> 1

要注意的是 Promise.resolve(go()) 这里也是普通函数/同步代码,当执行到这一行的时候,会立即执行 go(),后面的 .then() 才是微任务。

以上两道小题,是关于 Promise 的EventLoop分析。

遇到 async/await
根据定义,我们知道,async/await 仅仅是生成器的语法糖,所以不要怕,只要把它转换成 Promise 的形式即可。

async function foo() {console.log(1)await bar();console.log(2)
}
async function bar() {console.log(3)
}
foo();

可以转换成 Promise的形式,如下

 function foo(){console.log(1)Promise.resolve(bar()).then(()=>{console.log(2)})
}
function bar(){console.log(3)
}
foo()

执行步骤如下:

  • 执行 foo() ,打印同步代码 内容 1
  • 顺序执行 Promise.resolve() ,同步执行 bar() 方法,打印 3
  • Promise.thenr() 是一个微任务,将其放置到 微任务队列,此时微任务队列为[ 2 ]
  • 执行微任务队列,打印队列内容 2

所以执行的顺序是:

1 -> 3 -> 2

所以,开篇的那段代码,可改写成如下:

function async1() {console.log('async1 start'); // 2Promise.resolve(async2()).then(() => {console.log('async1 end'); // 6});
}function async2() {console.log('async2'); // 3
}console.log('script start'); // 1setTimeout(function() {console.log('settimeout'); // 8
}, 0);async1();new Promise(function(resolve) {console.log('promise1'); // 4resolve();
}).then(function() {console.log('promise2'); // 7
});
console.log('script end'); // 5
  • 首先打印出 script start
  • 接着将 settimeout 添加到宏任务队列,此时宏任务队列为 [‘settimeout’]
  • 然后执行函数 async1,先打印出 async1 start,又因为 Promise.resolve(async2()) 是同步任务,所以打印出 async2,接着将 async1 end 添加到微任务队列,,此时微任务队列为 [‘async1 end’]
  • 接着打印出 promise1,将 promise2 添加到微任务队列,,此时微任务队列为 [‘async1 end’, promise2]
  • 打印出 script end
  • 因为微任务优先级高于宏任务,所以先依次打印出 async1 end 和 promise2
  • 最后打印出宏任务 settimeout

script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2
-> settimeout

其实,上面的代码都弄清楚之后,对于EventLoop的执行机制就掌握的差不多了。

接下来是一道强化的题,有兴趣的可以看一下

 console.log('1')setTimeout(()=>{console.log('2');new Promise(resolve=>{console.log('3')resolve();}).then(()=>{console.log('4')})},0)new Promise(resolve=>{console.log('5')resolve();}).then(()=>{console.log('6')}) setTimeout(()=>{console.log('7');},0)   setTimeout(()=>{console.log('8');new Promise(resolve=>{console.log('9')resolve();}).then(()=>{console.log('10')})},0)new Promise(resolve=>{console.log('11')resolve();}).then(()=>{console.log('12')})console.log('13');

这里只把运行的答案写一下

1 -> 5 -> 11 -> 13 -> 6 -> 12 -> 2 -> 3 -> 4 -> 7 -> 8 -> 9 -> 10

EventLoop 事件循环机制相关推荐

  1. Eventloop事件循环机制

    第一次录文章的话,是有点小激动!哈哈哈 主要是看了一篇关于朋友分享的Eventloop,然后加上他独特的见解,所以之后说的理解其实有一半受了他的感悟 如果有什么地方说的不合适的话,请多多发我消息,不胜 ...

  2. 前端面试之事件循环机制

    一.事件循环是什么? 首先,JS是单线程的,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环机制.在JS中把任务分为同步和异步,同步任务和异步任务的执 ...

  3. 事件循环机制EventLoop

    事件循环机制EventLoop Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制. 一.EventLoop的相关概念 1.堆(Heap) 堆表示一大块非结构化的内存区 ...

  4. QT消息/事件循环机制与多线程的关系

    关于Qt子线程和消息循环 一.QT消息/事件循环机制 Qt作为一个可视化GUI界面操作系统,是基于事件驱动的,我们程序执行的顺序不再是线性,而是由一个个应用程序内部或外部的事件进行驱动,无事件时便阻塞 ...

  5. 浏览器事件循环机制与Vue nextTick的实现

    浏览器事件循环机制 先上一段简单的代码 console.log('aa'); setTimeout(() => { console.log('bb')}, 0); Promise.resolve ...

  6. 憨猫的EventLoop事件循环小解

    前言 两周没更新文章了,这次带来一篇关于 EventLoop 的知识点扫盲.在日常工作中还是框架库的底层中都涉及到了EventLoop(事件循环).那么这次就一起来看一下神奇的事件循环,它神秘的面纱究 ...

  7. JavaScript单线程异步的背后——事件循环机制

    感觉这篇文章拖了很久,好尴尬的拖延症 正文从这里开始--- 对JavaScript有个很模糊的印象,它是单线程异步的.本文主要来说说JavaScript到底是怎么运行的.但在这之前,让我们先理一下这些 ...

  8. boost log 能不能循环覆盖_前端基础进阶(十四):深入核心,详解事件循环机制...

    Event Loop JavaScript的学习零散而庞杂,很多时候我们学到了一些东西,但是却没办法感受到进步!甚至过了不久,就把学到的东西给忘了.为了解决自己的这个困扰,在学习的过程中,我一直在试图 ...

  9. js中如何得到循环中的点击的这个id_Js篇面试题9请说一下Js中的事件循环机制

    虽互不曾谋面,但希望能和您成为笔尖下的朋友 以读书,技术,生活为主,偶尔撒点鸡汤 不作,不敷衍,意在真诚吐露,用心分享 点击左上方,可关注本刊 标星公众号(ID:itclanCoder) 如果不知道如 ...

最新文章

  1. 将当前的head推送到远程_git 入门教程之远程仓库
  2. 互联网协议 — IPv4 — 分片与重组
  3. 招聘 | 香侬在召唤:算法工程师,让金融把你的代码变现
  4. [转]Listview的onItemClickListener无法响应的解决方法
  5. datasnap 2011连接池,数据集对象池
  6. cf1523C. Compression and Expansion
  7. pyqt5让主窗口居中显示(显示在显示器的中间位置)
  8. HTML5+CSS:左边导航栏,右边是对应的文本区域
  9. 【Python】数据转换利器
  10. php soap 下载文件,允许下载SOAP API响应(PHP)中的PDF文件get(作为附件)
  11. 一个简单的 javascript 中的正则表达式例子
  12. 专业PDF转换Word/PPT/Excel工具:AnyBizSoft PDF Converter绿色便携版
  13. 关于贝叶斯公式的解释,通俗易懂(转载)
  14. linux远程文件复制,Linux远程复制文件
  15. 掌握技巧可以快速完成网站备案
  16. oracle表示大于等于,这些基础请记住,java转义的问题:小于等于、 大于等于Mybatis 、oracle...
  17. 消防设施操作员考试真题、模拟练习题库(7)
  18. Mac重启Finder的技巧
  19. 解决win10访问xp服务器共享文件出现smb1问题
  20. 看看人家那系统设计做的,那叫一个优雅

热门文章

  1. linux 防火墙配置文件在哪里,linux 5.8 系统的防火墙配置文件在哪里?
  2. Photoshop CC 2019 Essential Training: Photography Photoshop CC 2019基本训练:摄影 Lynda课程中文字幕
  3. 思念水饺7月已知产品含病菌 专项咨询形同虚设
  4. telnet查看远程端口
  5. phpmailer 私密抄送_最详细的phpmailer的使用方法
  6. 神枪手 THE MARKSMAN
  7. spring中用到的设计模式及应用场景
  8. 面试结束后,被面试官在朋友圈吐槽了......
  9. 无许可的开源软件可以使用吗?
  10. <机器学习><详解>Ensemble 集成学习算法