前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Node异步I/O相关知识点(二)

Node异步I/O相关知识点(二)

作者头像
terrence386
发布2022-07-14 21:09:47
3520
发布2022-07-14 21:09:47
举报

当今社会,技术的作用已经越来越明显,未来社会中,谁掌握了先进的技术,谁就可以呼风唤雨。

前情回顾

上篇文章主要分享了异步I/O的阻塞,非阻塞问题,因为它们会对系统的性能有所影响。今天主要聊一下Node异步I/O中的事件循环和JS中的事件循环

JS的并发模型和事件循环

JavaScript有个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务,这个模型和其他语言截然不同。

想要理解这个模型,需要先理解这几个概念堆(heap),栈(stack),队列。在JS中,堆内存的作用在于提供引用类型的存储空间。栈内存的作用有两个:1,存放基本数据类型。2,提供代码的运行环境。提供运行环境其实是函数的调用形成了一个多帧组成的栈。如图(这个图有点秀,不要在意这些细节):

事件循环模型

看下面的代码:

代码语言:javascript
复制
function foo(b){
  let a = 10;
  return a+b;
}
function bar(x){
  return foo(x*y)
}
console.log(bar(7))

当调用bar()的时候,第一帧被压入栈中,包含了bar的参数和局部变量。当bar调用foo时,第二帧创建并压入栈中,放在第一帧上面,帧中包含foo的参数和局部变量。当foo执行完成后,第二帧就被弹出。当bar执行完成后,第一帧也被弹出,栈就清空了。

需要注意的是,一个JavaScript运行时包含了一个带处理消息的消息队列。每个消息都关联一个用于处理这个消息的回调函数。这个可以理解为上图底部的message

在js事件循环的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。

函数的处理会一直进行到执行栈再次为空为止,然后事件循环队列会处理队列中的下一个消息。

事件循环的实现代码大致如下:

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

queue.processNextMessage()会同步等待消息的到达。每个消息完整的执行完成后,其他消息才会被执行。

那么,消息是什么?这里可以理解为事件的回调函数。在浏览器中,每个事件发生并且有一个事件监听器绑定在该事件上时,一个消息就会被添加到消息队列。如果没有事件监听器,这个事件就会丢失。所以当一个带有点击事件处理器的元素被点击时,就会像其他事件一样产生一个类似的消息。

再说一下setTimeout,setTimeout函数接受两个参数:待加入队列的消息(即回调函数)和一个时间值。这个时间值代表的是这个消息(回调函数)被实际加到消息队列的最小延迟时间。如果队列中没有其他消息且执行栈为空,在这段时间过去后,消息会马上处理。但是如果有其他消息,setTimeout必须等到其他消息处理完成。所以,setTimeout的第二个参数仅仅代码最少延迟时间,并非确切的时间。

代码语言:javascript
复制
const s = new Date().getSeconds();
setTimeout(function(){
  console.log('run after '+ (new Date().getSeconds()-s) + 'seconds');

},500)

while(true){
  if(new Date().getSeconds()-s>=2){
    console.log('looped for 2 seconds')
    break;
  }
}
// looped for 2 seconds
// run after 2 seconds

可以看到执行代码后先输出了 looped for 2 seconds,然后输出run after 2 seconds。因为代码执行到setTimeout,发现他是一个消息,将它加入到了队列中,队列清空后,才又开始处理这个消息。

Node异步IO中的事件循环

Node自身的执行模型也叫事件循环。在进程启动时,Node会创建一个类似while(true)的循环,每执行一次循环体的过程被称为Tick。Tick的过程就是查看是否有事件待处理,如果有,就取出事件及其相关的回调函数。有关联的回调函数就执行它们。然后开始下个循环,如果没有事件,就退出进程。

那么在每次Tick的过程中,如何判断是否还有事件需要处理呢?这里就引入了观察者,个人的理解就是观察者模式,并且有可能Vue的实现也是借鉴了Node的这个理念。

非I/O的异步API

在面试的时候有时会问到异步的问题,最多的是promise相关的问题。涉及到的有setTimeout,process.nextTick,setImmediate相关的宏任务微任务的问题。这几个实际上是Node里的几个非I/O的异步API

setTimeout()setInterval()与浏览器中的API是一致的,分别用于单次和多次定时执行任务。它们的实现原理与异步I/O比较类似,只是不需要I/O线程池的参与。调用setTimeout()或者setInterval()创建的定时器会被插入到定时器观察者内部的一个红黑树中。每次Tick执行时,会从该红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成一个事件,它的回调函数将立即执行。

总结

  • js中的事件循环机制
  • Node中的事件循环

javascript基础知识总结

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

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

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

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