前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >js执行栈与事件循环简单理解

js执行栈与事件循环简单理解

原创
作者头像
brzhang
发布2021-08-18 12:10:16
1.6K0
发布2021-08-18 12:10:16
举报
文章被收录于专栏:玩转全栈玩转全栈

JavaScript 是如何异步和单线程的,这个是大多数人知道的一句话,但是理解其真正原理的并不是太多....

简单的来说,JavaScript语言是单线程的,但是他表现出的异步行为并不是语言层面的东西,而是依赖于浏览器内核的,这种异步的特性是通过浏览器API表达出来的。浏览器API内置在Web 浏览器中,它们不是 JavaScript 语言本身的一部分,而是建立在核心 JavaScript 语言之上,可以访问用户机器底层能力的API,比如,访问用户的定位信息,这个实际上背后可能是C++的一些实现,拿到定位数据,返回给到浏览器。

image-20210817213113895
image-20210817213113895
执行栈

执行栈简单的来说,就是执行函数的堆栈,举个例子:

代码语言:javascript
复制
function main(){
  console.log('A');
  setTimeout(
    function display(){ console.log('B'); }
  ,0);
  console.log('C');
}
main();
//  Output
//  A
//  C
//  B

以上函数执行的过程就可以用下面的执行栈来表现出来:

  1. 首先main函数作为入口函数,肯定是第一个优先放到执行栈中的;
  2. console.log('A');是一个函数,虽然用得很多,但是确实是执行一个函数,想console控制台输出文本‘A’,在main函数没执行完时,他就被压入栈中,这很好理解,main函数执行完成必须依赖他的执行。由于console.log('A');是一个不依赖任何其他函数的语句,因此,他一上场就被秒杀了。
  3. 随后setTimeout( function display(){ console.log('B'); } ,0);被压入栈中,在main的上面,同理,他被执行了,但是他的执行似乎不同,留下了一个尾巴,function display(){ console.log('B'); },他被Browser Api搜集,并且丢到了 Message Queue中去了,但实际上setTimeout这个函数确实是执行完毕了,因此他退出了栈。
  4. 随后 console.log('C');被压入栈执行,不依赖任何函数,因此也被秒杀了
  5. 然后main函数走到了人生的尽头,此时执行栈空了!!!,重要事情说三遍,执行栈空了!!!执行栈空了!!!执行栈空了!!!
  6. 所以,执行栈空了,此时 Message Queue才有了释放自己收集的任务的机会,此时,function display(){ console.log('B'); }这个函数里的执行体被压入到执行栈中去执行,其实就是console.log('B')

所以 setTimeout(function, delayTime) 中的延迟参数并不代表函数执行后的精确时间延迟。它代表最短等待时间,在此之后的某个时间点将执行该函数,也可以好不怀疑的说,等执行栈空了,他才有机会出现表现自己

事件循环

所以,事件循环其实就是js代码借助与浏览器API向消息队列中丢入一些回调函数,等待执行栈放空自己的时候,把消息队列中的回调函数压入到执行栈中执行的这么一个机制。

为了证明我的结论所言不虚,我就用一个极端的例子来标明下:

代码语言:javascript
复制
function main(){
  console.log('A');
  setTimeout(
    function exec(){ console.log('B'); }
  , 0);
  runWhileLoopForNSeconds(3);
  console.log('C');
}
main();
function runWhileLoopForNSeconds(sec){
  let start = Date.now(), now = start;
  while (now - start < (sec*1000)) {
    now = Date.now();
  }
}
// Output
// A
// C
// B
  1. 起初main函数被压入栈中执行,在执行过程中,发现有内容 console.log('A');
  2. 于是,main得不到退出,console.log('A');被压入执行栈中,然后立即执行了,打印了A,紧接着,
  3. setTimeout( function exec(){ console.log('B'); } , 0);被压入执行栈中,setTimeout函数也被立即执行了,但由于其特殊性,浏览器API将其包裹的回调函数交给了消息队列。因为此时执行中中mian函数还没有执行玩,所以,消息队列里面的消息只能望穿秋水的等待着....
  4. 紧接着runWhileLoopForNSeconds(3);被压入了执行栈中,是一个函数,由于js是单线程的,因此mian也好,runWhileLoopForNSeconds也好,都会在这个执行栈所在在执行上线文中孤独的执行着,不受其他人影响,所以,runWhileLoopForNSeconds默默的执行了3s,终于执行玩,然后看下main函数执行玩没,还没有,还有 console.log('C');没执行
  5. 所以,console.log('C');被压入了执行栈,然后秒执行了,此时main总算走空了,因此事件循环现在就看消息队列中有没有消息了,已看发现有,嘿,一个一个的丢出来,放到执行栈中来执行。
总结

所以,只有当执行栈中是空的时候,事件循环机制才有机会把消息队列中的任务丢出来执行,换句话说,只有执行栈中有内容在执行,事件循环就不可能给你从消息队列中取任务出来执行。如果英语比较好,可以看看这个视频,一瞬间秒懂 https://www.youtube.com/watch?v=8aGhZQkoFbQ

附录&参考资料

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 执行栈
  • 事件循环
  • 总结
  • 附录&参考资料
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档