精华内容
下载资源
问答
  • Event Loop事件循环机制 事件循环机制(Event Loop)是全面了解javascript代码执行顺序绕不开的一个重要知识点。 为什么会有事件循环机制? javascript从诞生之日起就是一门单线程的非阻塞的脚本语言。这是由其最初...
  • 使用Qt实现事件循环 作者: 马克·哈维斯顿(Mark Harviston)< >,克努森(Arve Knudsen)< > 要求 Quamash需要具有asyncio移植的asyncio库和PyQt4,PyQt5或PySide的Python 3.4或Python 3.3。 安装 pip ...
  • 这带来了一个实际的问题:在事件循环已经在运行的环境中,无法运行任务并等待结果。 尝试这样做将产生错误“ RuntimeError: This event loop is already running ”。 该问题在各种环境中弹出,例如Web服务器,GUI...
  • 库可用于事件I / O的的核心React器事件循环。 为了使基于异步的库可互操作,它们需要使用相同的事件循环。 该组件提供任何库都可以定位的通用LoopInterface 。 这使它们可以在同一循环中使用,并由用户控制一个...
  • 主要介绍了JS浏览器事件循环机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 事件循环

    千次阅读 2020-04-01 09:49:31
    事件触发线程 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助) 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步...

    打开一个tab页面就有一个渲染进程!!!

    进程与线程

    进程和线程的区别:

    • 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
    • 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
    进程是一个工厂,工厂有它的独立资源
    工厂之间相互独立
    线程是工厂中的工人,多个工人协作完成任务
    工厂内有一个或多个工人
    工人之间共享空间
    

    请牢记,浏览器的渲染进程是多线程的。主要有以下几个线程

    1. GUI渲染线程

    负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

    注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

    1. JS引擎线程

    也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)

    JS引擎线程负责解析Javascript脚本,运行代码。

    JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序

    同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

    1. 事件触发线程

    归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)

    当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中

    当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

    1. 定时触发器线程

    传说中的setInterval与setTimeout所在线程

    浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)

    因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
    注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

    1. 异步http请求线程

    在XMLHttpRequest在连接后是通过浏览器新开一个线程请求

    将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
    在这里插入图片描述
    如果你了解 web worker ,那么 worker 线程也是其中之一。

    可以参考这篇文章了解各个线程的作用:连接

    注意:GUI渲染线程,JS引擎线程是互斥的!!!

    由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

    JS在执行的过程中页面是停止渲染的!!!

    浏览器渲染流程

    浏览器内核拿到内容后,渲染大概可以划分成以下几个步骤:

    1. 解析html建立dom树
    2. 解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)
    3. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
    4. 绘制render树(paint),绘制页面像素信息
    5. 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

    在这里插入图片描述

    JS的运行机制

    了解浏览器渲染流程,还要了解JS的运行机制。JS的运行机制就是事件循环

    JS引擎是单线程,负责执行JS代码的。

    1. JS分为同步任务和异步任务
    2. 同步任务都在主线程上执行,形成一个执行栈
    3. 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
    4. 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

    在这里插入图片描述

    在进程启动时,便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。每个Tick的过程就是查看是否有事件待处理,如果有,就取出事件及其相关的回调函数。如果存在关联的回调函数,就执行它们。

    宏任务与微任务

    在这里插入图片描述
    上图大致描述就是:

    1. 主线程运行时会产生执行栈,

    栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)

    1. 而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调
    2. 如此循环
    3. 注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

    再根据线程来理解下:

    宏任务都是放在一个事件队列中的,而这个队列由事件触发线程维护

    微任务都是添加到微任务队列(Job Queues)中,等待当前宏任务执行完毕后执行,而这个队列由JS引擎线程维护

    所以,总结下运行机制:

    1. 执行一个宏任务(栈中没有就从事件队列中获取)
    2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
    3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
    4. 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
    5. 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

    如图:
    在这里插入图片描述

    JS哪些API是宏任务,哪些是微任务?

    宏任务(macrotask)::

    setTimeout、setInterval、postMessage、 MessageChannel、setImmediate

    微任务(microtask):

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

    展开全文
  • 详解node.js 事件循环

    2020-10-15 02:10:21
    主要介绍了node.js 事件循环的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
  • 主要介绍了浅谈Node 异步IO和事件循环,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了JavaScript运行机制之事件循环(Event Loop)详解,本文从多个方面讲解了Event Loop,需要的朋友可以参考下
  • nodejs事件和事件循环简介

    万次阅读 2020-12-06 21:37:03
    我们在javascript中监听这些事件,从而触发相应的处理。 同样的nodejs中也有事件,并且还有一个专门的events模块来进行专门的...同时事件和事件循环也是nodejs构建异步IO的非常重要的概念。 今天我们来详细了解一下。

    简介

    熟悉javascript的朋友应该都使用过事件,比如鼠标的移动,鼠标的点击,键盘的输入等等。我们在javascript中监听这些事件,从而触发相应的处理。

    同样的nodejs中也有事件,并且还有一个专门的events模块来进行专门的处理。

    同时事件和事件循环也是nodejs构建异步IO的非常重要的概念。

    今天我们来详细了解一下。

    事件

    nodejs为事件提供了一个专门的模块:lib/events.js。

    还记得我们在讲使用nodejs构建web服务器吗?

    const server = http.createServer((req, res) => {
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/plain')
      res.end('welcome to www.flydean.com\n')
    })
    

    这里,每个请求都会触发request事件。

    nodejs的核心API是基于异步事件驱动来进行架构的,所以nodejs中有非常多的事件。

    比如:net.Server 会在每次有新连接时触发事件,fs.ReadStream 会在打开文件时触发事件,stream会在数据可读时触发事件。

    我们看一下怎么来构建一个nodejs的事件:

    const EventEmitter = require('events')
    const eventEmitter = new EventEmitter()
    

    events常用的方法有两个,分别是on和emit。

    on用来监听事件,emit用来触发事件。

    eventEmitter.on('fire', () => {
      console.log('开火')
    })
    
    eventEmitter.emit('fire')
    

    emit还可以带参数,我们看下一个参数的情况:

    eventEmitter.on('fire', who => {
      console.log(`开火 ${who}`)
    })
    
    eventEmitter.emit('fire', '美帝')
    

    再看看两个参数的情况:

    eventEmitter.on('fire', (who, when) => {
      console.log(`开火 ${who} ${when}`)
    })
    
    eventEmitter.emit('fire', '川建国''now')
    

    默认情况下,EventEmitter以注册的顺序同步地调用所有监听器。这样可以确保事件的正确排序,并有助于避免竞态条件和逻辑错误。

    如果需要异步执行,则可以使用setImmediate() 或者 process.nextTick()来切换到异步执行模式。

    eventEmitter.on('fire', (who, when) => {
        setImmediate(() => {
          console.log(`开火 ${who} ${when}`);
      });
    })
    
    eventEmitter.emit('fire', '川建国''now')
    

    除此之外,events还支持其他几个方法:

    once(): 添加单次监听器

    removeListener() / off(): 从事件中移除事件监听器

    removeAllListeners(): 移除事件的所有监听器

    事件循环

    我们知道nodejs的代码是运行在单线程环境中的,每次只会去处理一件事情。

    这一种处理方式,避免了多线程环境的数据同步的问题,大大的提升了处理效率。

    所谓事件循环,就是指处理器在一个程序周期中,处理完这个周期的事件之后,会进入下一个事件周期,处理下一个事件周期的事情,这样一个周期一个周期的循环。

    事件循环的阻塞

    如果我们在事件处理过程中,某个事件的处理发生了阻塞,则会影响其他的事件的执行,所以我们可以看到在JS中,几乎所有的IO都是非阻塞的。这也是为什么javascript中有这么多回调的原因。

    事件循环举例

    我们看一个简单的事件循环的例子:

    const action2 = () => console.log('action2')
    
    const action3 = () => console.log('action3')
    
    const action1 = () => {
        console.log('action1')
        action2()
        action3()
    }
    
    action1()
    

    上面的代码输出:

    action1
    action2
    action3
    

    栈和消息队列

    我们知道函数间的调用是通过栈来实现的,上面的例子中,我们的调用顺序也是通过栈来实现的。

    但并不是函数中所有的方法都会入栈,还有一些方法会被放入消息队列。

    我们再举一个例子:

    const action2 = () => console.log('action2')
    
    const action3 = () => console.log('action3')
    
    const action1 = () => {
        console.log('action1')
        setTimeout(action2, 0)
        action3()
    }
    
    action1()
    

    上面的代码运行结果:

    action1
    action3
    action2
    

    结果不一样了。这是因为settimeout触发了定时器,当定时器到期的时候,回调函数会被放入消息队列中等待被处理,而不是放入栈中。

    事件循环会优先处理栈中的事件,只有栈中没有任何数据的时候,才会去转而消费消息队列中的事件。

    虽然上面例子中setTimeout的timeout时间是0,但是还是要等到action3执行完毕才能执行。

    注意,setTimeout中的timeout并不是在当前线程进行等待的,它是由浏览器或者其他JS执行环境来调用的。

    作业队列和promise

    ES6中的Promise引入了作业队列的概念,使用作业队列将会尽快地执行异步函数的结果,而不是放在调用堆栈的末尾。

    举个例子:

    const action2 = () => console.log('action2')
    
    const action3 = () => console.log('action3')
    
    const action1 = () => {
        console.log('action1')
        setTimeout(action2, 0)
        new Promise((resolve, reject) =>
            resolve('应该在action3之后、action2之前')
        ).then(resolve => console.log(resolve))
        action3()
    }
    
    action1()
    

    输出结果:

    action1
    action3
    应该在action3之后、action2之前
    action2
    

    这是因为,在当前函数结束之前 resolve 的 Promise 会在当前函数之后被立即执行。

    也就是说先执行栈,再执行作业队列,最后执行消息队列。

    process.nextTick()

    先给大家一个定义叫做tick,一个tick就是指一个事件周期。而process.nextTick()就是指在下一个事件循环tick开始之前,调用这个函数:

    process.nextTick(() => {
      console.log('i am the next tick');
    })
    

    所以nextTick一定要比消息队列的setTimeout要快。

    setImmediate()

    nodejs提供了一个setImmediate方法,来尽快的执行代码。

    setImmediate(() => {
      console.log('I am immediate!');
    })
    

    setImmediate中的函数会在事件循环的下一个迭代中执行。

    setImmediate() 和 setTimeout(() => {}, 0)的功能基本上是类似的。它们都会在事件循环的下一个迭代中运行。

    setInterval()

    如果想要定时执行某些回调函数,则需要用到setInterval。

    setInterval(() => {
      console.log('每隔2秒执行一次');
    }, 2000)
    

    要清除上面的定时任务,可以使用clearInterval:

    const id = setInterval(() => {
      console.log('每隔2秒执行一次');
    }, 2000)
    
    clearInterval(id)
    

    注意,setInterval是每隔n毫秒启动一个函数,不管该函数是否执行完毕。

    如果一个函数执行时间太长,就会导致下一个函数同时执行的情况,怎么解决这个问题呢?

    我们可以考虑在回调函数内部再次调用setTimeout,这样形成递归的setTimeout调用:

    const myFunction = () => {
      console.log('做完后,隔2s再次执行!');
    
      setTimeout(myFunction, 2000)
    }
    
    setTimeout(myFunction, 2000)
    

    本文作者:flydean程序那些事

    本文链接:http://www.flydean.com/nodejs-event/

    本文来源:flydean的博客

    欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

    展开全文
  • 循环是我们在日常开发中是必不可少会遇到的,下面这篇文章主要给大家介绍了关于在Python的一段程序中如何使用多次事件循环的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
  • 适用于Linux的简单事件循环简介API创建事件上下文注册事件观察程序使用-luev Joystick示例启动事件循环摘要生成并安装Origin&am µEv | 适用于Linux的简单事件循环简介API创建事件上下文使用-luev Joystick示例注册...
  • tokio:以Rust语言编写的Asyncio事件循环
  • node和浏览器都是js运行的环境,二者都给js提供了一个很强大的功能,事件循环。那么什么是事件循环呢,简单讲就是在单线程中为了实现程序的高效运行而设定的事件执行机制 ...

    技术背景

    javaScript事件循环(Event Loop)是其作为单线程语言但能实现高效异步运行的核心基础。要想更深入的了解js,对事件循环就要有一个清楚地认识。在node出现之后,js的运行环境不再是单一的浏览器,同样的,node中在有事件循环。那么到底什么是事件循环呢?写事件循环的文章有很多,但是对于一些初学者看起来却有些不好理解,本文试着对event loop做一个有表象到宏观的认识,希望把事件循环表述的更加形象化。首先了解几个背景问题。

    几个背景问题:

    1,什么是线程,进程,二者有什么区别和联系,

    进程是计算机系统资源分配的最小单位,该最小单位相互之间有独立的内存,进程有独立的内存空间;

    线程是计算机cpu调度分配的最小单位,调度最小单位是cpu中可以独立运行的最小基本单位,线程没有属于自己的内存空间;

    一个进程可以有很多线程,每条线程并行执行不同的任务。

    形象化理解:

    把计算机看做一家公司

    进程就是独立的部门,每个部门有自己的资源;

    线程就是每个部门的员工,每个员工没有资源,是最小的干活单位,共享部门资源;

    2,js为什么是单线程的

    这主要和js的用途有关,在node出现之前,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。

    举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措...

    单线程和事件循环(event loop)

    javaScript是单线程的,在执行代码时只能按顺序执行,为了解决代码执行时的阻塞,所以js是异步的,比如在遇到setTimeout时,不会定时器内容执行过后,再去执行之后的代码,而是先执行代码,等时间到后再去执行定时器。

    基于这种异步的机制,javaScript有着一套自己执行代码的规则,来保证代码能够高效无阻塞的运行,这种规则就是事件循环。

    node和浏览器都给js提供了运行的环境,但是二者的运行机制是稍有差异的。

    浏览器js运行机制

    不同的浏览器有不同的js引擎

    虽然浏览器不同,不过起内部的事件循环规则是一致的。

    node.js运行机制

    node.js采用v8作为js的解析引擎,而在I/O处理方面采用了libuv。

    libuv库负责node api的执行,它将不统的任务分给不同的线程,形成一个Event Loop。以异步的方式将执行的结果返回给V8引擎。

    浏览器中的Event Loop

    直接用文字描述Event Loop理解起来是比较费脑的,首先先介绍下Event Loop的相关概念。

    Event Loop包括了 执行栈, 事件队列, 微任务,宏任务,执行栈和事件队列是事件循环中存储事件的地址,微任务和宏任务是事件循环中执行的事件。

    执行栈:

    js整体代码加载过后,会进入运行,这时会产生一个执行上下文(context),当代码执行完毕之后,该执行上下文被释放。对于一个执行上下文,也可以称为当前js执行环境,包括了私有作用域,当前作用域中的变量,上层作用域,当前作用域对象this

    由于js是单线程的,在执行前面的代码时,后面的代码等待执行,此时该部分代码(函数或者可直接执行的代码)被放到一个栈中,称为执行栈。

    事件队列:

    上面js的运行只考虑了同步事件,当js执行过程中遇到了异步事件(或者定时事件),会把对应的这些事件挂起,js并将这个事件加入与当前执行栈不同的另一个队列,继续执行当前执行上下文中的同步代码,这个存储异步事件的队列称为事件队列。

    在一个执行环境中的执行栈清空之后,js此时会去查看事件队列是否为空,不为空,则继续执行这些事件。

    执行事件队列中的事件时,会遵循执行栈的规则,首先会生成一个对应当前事件的执行上下文,然后生成执行栈,事件队列,当该执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。接着执行事件队列中的下一个事件,规则一样。当事件队列清空后,外层执行环境被销毁,执行结束。

    从上面的分析中可以看出,事件循环指的是在事件队列中执行代码重复了外层执行栈的规则,一层一层深入,就形成了循环。

    两个注意点:

    1,同一个执行上下文中同步任务优先于异步任务

    2,不同执行环境中的异步任务执行先后取决于其加入到事件队列的时间先后

    3,事件队列在不同执行环境中是同一个

    例子:

    function a(){
        console.log('a')
        setTimeout( () => {
            console.log('a1')
        },0)
    }
    
    
    function b(){
        console.log('b')
        setTimeout( () => {
            console.log('b1')
        },0)
    }
    
    
    setTimeout( () => {
        console.log('上层')
    },0)
    
    a()
    b()
    
    // 运行结果
    // a
    // b
    
    // 上层
    // a1
    // b1

    改变时间

    function a(){
        console.log('a')
        setTimeout( () => {
            console.log('a1')
        },100)
    }
    
    
    function b(){
        console.log('b')
        setTimeout( () => {
            console.log('b1')
        },0)
    }
    
    
    setTimeout( () => {
        console.log('上层')
    },10)
    
    a()
    b()
    
    // 运行结果
    // a
    // b
    // b1
    // 上层
    // a1
    

    形象化理解Event Loop:

    把js代码中的方法看成是医院看病的病人

    执行栈是所有的病人,按顺序就诊

    同步任务是直接就诊的病人

    异步任务是需要化验的病人,拿到结果后就诊

    对于循环

    需要化验的病人如果是一个团体,后面排队的病人等待这个团体直接就诊的病人都看完后,就诊下一个团体,循环就诊。

     

    微任务:

    • new Promise()
    • new MutaionObserver()

    宏任务:

    • setInterval()
    • setTimeout()
    • 整体代码
    • I/O 操作、UI 渲染

    在一个事件循环中,异步事件返回结果后会被放到一个任务队列中。然而,根据这个异步事件的类型,这个事件实际上会被对应的宏任务队列或者微任务队列中去。并且在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果不存在,那么再去宏任务队列中取出一个事件并把对应的回到加入当前执行栈;如果存在,则会依次执行队列中事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出最前面的一个事件,把对应的回调加入当前执行栈...如此反复,进入循环。

    总结:事件队列分为微任务队列,宏任务队列,同一次事件循环中,微任务永远在宏任务之前执行。

    形象化理解:

    微任务是需要化验的急诊病人

    宏任务是需要化验的普通病人

    node中的Event Loop

    从node的运行机制我们知道,ibuv库负责node api的执行

    每个阶段的含义:

    • timers: 这个阶段执行定时器队列中的回调如 setTimeout() 和 setInterval()
    • I/O callbacks: 这个阶段执行几乎所有的回调。但是不包括close事件,定时器和setImmediate()的回调。
    • idle, prepare: 这个阶段仅在内部使用,可以不必理会。
    • poll: 等待新的I/O事件,node在一些特殊情况下会阻塞在这里。
    • check: setImmediate()的回调会在这个阶段执行。
    • close callbacks: 例如socket.on('close', ...)这种close事件的回调。

    执行顺序

    当个v8引擎将js代码解析后传入libuv引擎后,循环首先进入poll阶段,poll阶段相当于整体同步代码的解析,会生成一个执行栈,同时会把setImmediate的回调放入check队列,在setTimeout() 和 setInterval()定时到期后把其回调事件放入timers队列,poll queue清空后,会转到check阶段,检查执行check队列,检查执行timer队列,之后进入到callbacks阶段,执行回调。check和timer两者的顺序是不固定的,受到代码运行的环境的影响。

    进入一个新阶段之后,会重复上面各个阶段,直至执行完毕,进入下一个阶段。

    总结:

    poll轮询属于io观察者,process.nextTick()属于idle观察者, setImmediate()属于check观察者。

    在每一轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者.

    第一次进入代码时,idle观察者不存在。

    Node中的process.nextTick()

    process.nextTick()是node中一个特殊的队列,这些事件会在每一个阶段执行完毕准备进入下一个阶段时优先执行。也就是说process.nextTick()在阶段切换时执行。并且,不管其有多深的回调,都会被一次执行完毕。

    Promise

    上面的阶段没有包括Promise,在node中,promise和浏览器中类似,执行在process.nextTick()之后,在setTimeout之前

    实例:

    const fs = require('fs')
    const path = require('path')
    
    const wait = () => new Promise((resolove, reject) => {
      setTimeout(resolove(true), 3)
    })
    fs.readFile(path.resolve(__dirname, './vue.config.js'), 'utf-8', async (err, data) => {
      console.log('读取的文件内容')
      await wait()
      console.log('测试测试')
      process.nextTick(() => {
        console.log('nextTick')
      })
    })
    
    setTimeout(() => {
      console.log('定时器任务0')
    }, 0)
    
    
    setTimeout(() => {
      console.log('定时器任务100')
    }, 1000)
    
    
    setImmediate(() => {
      console.log('立即执行')
    })
    
    Promise.resolve().then(() => {
      console.log('promise')
    })
    
    process.nextTick(() => {
      console.log('外层nextTick')
    })
    
    console.log('外层同步')
    // 运行结果
    // 外层同步
    // 外层nextTick
    // promise
    // 定时器任务0
    // 立即执行
    // 读取的文件内容
    // 测试测试
    // nextTick
    // 定时器任务100

    形象化理解

    固执的探险家(每个房间都要走到底)

    所有的代码就像是已经设定好的迷宫,而引擎就是去探险的人,我们称为小呆。

    小呆到达迷宫,已经有了地图,根据地图冒险。

    迷宫有六个房间,分别是timer, i/ocallback, ide prepare(内部使用,已封闭), poll, check, close callback,

    其中timer是虚拟现实房间,小呆随时可以看到里面的场景。

    其他的每个房间又五个房间,有的开放,有的不开放。

    探险规则:每次离开一个房间,都要检查有没有受伤(peocess.nextTick()

    小呆首先进入poll房间,开始探险(执行poll 队列),之后进入check房间,timer房间(随机),探险完之后出来,进入close callback,探险完之后,进入io/callback房间,最后完成探险,离开。

    小呆说任务总算完成了。

    参考:

    https://zhuanlan.zhihu.com/p/33058983

    https://zhuanlan.zhihu.com/p/54882306

     

     

     

    展开全文
  • 事件循环桥接到并行事件 安装 要通过安装,请使用以下命令,它将自动检测最新版本并将其与~绑定。 composer require react-parallel/event-loop 用法 设置 就像ReactPHP事件循环一样,您应该只有一个网桥。 您可以...
  • JS浏览器事件循环机制

    千次阅读 2020-05-29 17:16:22
    第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否存在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列...

    进程、线程

    进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。

    线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。

    浏览器内核

    浏览器是多进程的,浏览器每一个 tab 标签都代表一个独立的进程(也不一定,因为多个空白 tab 标签会合并成一个进程),浏览器内核(浏览器渲染进程)属于浏览器多进程中的一种。

    浏览器内核有多种线程在工作。

    GUI 渲染线程:

    负责渲染页面,解析 HTML,CSS 构成 DOM 树等,当页面重绘或者由于某种操作引起回流都会调起该线程。
    和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 任务队列中,等待 JS 引擎线程空闲的时候继续执行。

    JS 引擎线程:

    单线程工作,负责解析运行 JavaScript 脚本。
    和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。

    事件触发线程:

    当事件符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待 JS 引擎处理。

    定时器触发线程:

    浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不准确。
    开启定时器触发线程来计时并触发计时,计时完成后会被添加到任务队列中,等待 JS 引擎处理。

    http 请求线程:

    http 请求的时候会开启一条请求线程。
    请求完成有结果了之后,将请求的回调函数添加到任务队列中,等待 JS 引擎处理。

    在这里插入图片描述

    JavaScript 引擎是单线程

    JavaScript 引擎是单线程,也就是说每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后才会往下执行下一个任务。

    HTML5 中提出了 Web-Worker API,主要是为了解决页面阻塞问题,但是并没有改变 JavaScript 是单线程的本质。了解 Web-Worker。

    JavaScript 事件循环机制

    JavaScript 事件循环机制分为: 浏览器和 Node 事件循环机制,
    两者的实现技术不一样:
    浏览器 Event Loop 是 HTML 中定义的规范,
    Node Event Loop 是由 libuv 库实现。

    这里主要讲的是浏览器部分。

    Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

    JS 调用栈

    JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被添加到栈中的顶部,执行完成之后就从栈顶部移出该函数,直到栈内被清空。

    同步任务、异步任务

    JavaScript 单线程中的任务分为同步任务和异步任务。

    同步任务会在调用栈中按照顺序排队等待主线程执行,
    异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候,也就是栈内被清空的时候,被读取到栈中等待主线程执行。
    任务队列是先进先出的数据结构。

    Event Loop

    调用栈中的同步任务都执行完毕,栈内被清空了,
    就代表主线程空闲了,
    这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空,都会去读取任务队列有没有任务,有就读取执行,一直循环读取-执行的操作,就形成了事件循环。

    在这里插入图片描述

    在这里插入图片描述

    定时器

    定时器会开启一条定时器触发线程来触发计时,定时器会在等待了指定的时间后将事件放入到任务队列中等待读取到主线程执行。

    定时器指定的延时毫秒数其实并不准确,因为定时器只是在到了指定的时间时将事件放入到任务队列中,必须要等到同步的任务和现有的任务队列中的事件全部执行完成之后,才会去读取定时器的事件到主线程执行,中间可能会存在耗时比较久的任务,那么就不可能保证在指定的时间执行。

    宏任务(macro-task)、微任务(micro-task)

    除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。

    macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

    micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。

    console.log(1);
    setTimeout(function() {
        console.log(2);
    })
    var promise = new Promise(function(resolve, reject) {
        console.log(3);
        resolve();
    })
    promise.then(function() {
        console.log(4);
    })
    console.log(5);
    
    

    示例中,setTimeout 和 Promise被称为任务源,来自不同的任务源注册的回调函数会被放入到不同的任务队列中。

    有了宏任务和微任务的概念后,那 JS 的执行顺序是怎样的?是宏任务先还是微任务先?

    第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否存在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。

    上面的示例中,第一次事件循环,整段代码作为宏任务进入主线程执行。
    遇到了 setTimeout ,就会等到过了指定的时间后将回调函数放入到宏任务的任务队列中。
    遇到 Promise,将 then 函数放入到微任务的任务队列中。
    整个事件循环完成之后,会去检测微任务的任务队列中是否存在任务,存在就执行。

    第一次的循环结果打印为: 1,3,5,4。

    接着再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。
    检测到宏任务队列中已经没有了要执行的任务,那么就结束事件循环。
    最终的结果就是 1,3,5,4,2。

    node.js的Event Loop

    Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。

    node.js运行机制:

    1.v8引擎解析JavaScript脚本。

    2.解析后的代码,调用Node API。

    3.libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。

    4.V8引擎再将结果返回给用户。

    node只是在应用层属于单线程,底层其实通过libuv维护了一个阻塞I/O调用的线程池。

    JS之所以可以在执行异步操作时可以设置好callback函数之后,就继续执行后续代码,当异步操作执行完成时,又能够及时触发回调函数,获取异步结果。最关键的核心就在于事件循环。

    在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程被称为tick,中文翻译应该意为“滴答”,就像时钟一样,每滴答一下,就表示过去了1s。

    Tick:

    这个tick也有点这个意思,每循环一次,都表示本次tick结束,下次tick开始。
    每个tick开始之初,都会检查是否有事件需要处理,
    如果有,就取出事件及关联的callback函数,如果存在有关联的callback函数,就把事件的结果作为参数调用这个callback函数执行。
    如果不在有事件处理,就退出进程。

    那么在每个tick的过程中,如何判断是否有事件需要处理,先要引入一个概念,叫做“观察者”(watcher)。每一个事件循环都有一个或者多个观察者,判断是否有事件要处理的过程就是向这些观察者询问是否有需要处理的事件

    Node的观察者有这样几种:

    idle观察者:顾名思义,就是早已等在那里的观察者,以后会说到的process.nextTick就属于这类

    I/O观察者:顾名思义,就是I/O相关观察者,也就是I/O的回调事件,如网络,文件,数据库I/O等

    check观察者:顾名思义,就是需要检查的观察者,后面会说到的setTimeout/setInterval就属于这类

    事件循环是一个典型的生产者/消费者模型。
    异步I/O,网络请求,setTimeout等都是典型的事件生产者,源源不断的为Node提供不同类型的事件,这些事件被传到对应的观察者那里,事件循环在每次tick时则从观察者那里取出事件并处理。

    我们现在知道,JavaScript的异步I/O调用过程中,回调函数并不由我们开发者调用,
    事实上,在JavaScript发起调用到内核执行完I/O操作的过程中,

    存在一种中间产物,它叫做请求对象

    这个请求对象会重新封装回调函数及参数,并做一些其他的处理。这个请求对象,会在异步事件完成时被调用,取出回调函数和参数,并传入执行结果进行回调。

    组装好请求对象,送入I/O线程池等待执行

    实际上只是完成了异步I/O的第一步;
    第二步则是异步I/O被线程池处理结束后的回调,也就是执行回调。

    因为事件循环的逻辑就是这样,这里就不手工再重新绘制一张异步I/O流程图,直接把朴老师书里面的图贴上来给大家看。

    在这里插入图片描述

    应该说,事件循环、观察者、请求对象、I/O线程池,这四者共同组成了Node异步I/O模型的基本要素。

    在这里插入图片描述

    总结

    根据源码分析图,可以请求出得出这样几个结论:

    1、每次tick都会检查所有完成的回调事件,并一一执行回调函数。

    2、跑完当前执行环境下能跑完的代码。每一个事件消息都被运行直到完成为止,在此之前,任何其他事件都不会被处理。这和C等一些语言不通,它们可能在一个线程里面,函数跑着跑着突然停下来,然后其他线程又跑起来了。JS这种机制的一个典型的坏处,就是当某个事件处理耗时过长时,后面的事件处理都会被延后,直到这个事件处理结束,在浏览器环境中运行时,可能会出现某个脚本运行时间过长,页面无响应的提示。Node环境则可能出现大量用户请求被挂起,不能及时响应的情况。

    3、不同类型的观察者,处理的优先级不同,idle观察者最先,I/O观察者其次,check观察者最后。

    展开全文
  • 小柒前面总结了几篇关于浏览器的事件循环,这篇文章主要总结Node的事件循环。 Node中的Event Loop 什么是Node.js Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度...
  • 深入理解JavaScript的事件循环(Event Loop) 一、什么是事件循环 JS的代码执行是基于一种事件循环的机制,之所以称作事件循环,MDN给出的解释为因为它经常被用于类似如下的方式来实现 while (queue....
  • nodejs是单线程(single thread)运行的,通过一个事件循环(event-loop)来循环取出消息队列(event-queue)中的消息进行处理,处理过程基本上就是去调用该消息对应的回调函数。消息队列就是当一个事件状态发生变化时,就...
  • 1.JavaScript中事件循环 可以参考《JavaScript忍者秘籍第二版》第十三章,讲解的很好。 JavaScript中事件循环,主要就在理解宏任务和微任务这两种异步任务。 宏任务(macrotask): setTimeOut 、 setInterval 、 ...
  • 微信小程序 Node.js (基础四) 事件循环 Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。 Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发...
  • twisted:使用事件驱动机制来提升python性能 gevent:使用greenlet在用户态完成栈和上下文切换来减少切换带来的性能损耗 tornado:使用生成器来保存协程上下文及状态,使用原生的python语法实现了协程 asyncio:异步...
  • 本文详细的介绍了Node.js 事件循环和Node.js回调函数,废话不多说了,具体看下面把。  一、Node.js 事件循环 Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。Node.js 的每一个 API...
  • 本文实例讲述了node事件循环和process模块。分享给大家供大家参考,具体如下: 1.node.js事件循环 node.js事件可以继续插入事件,如果有事件就继续执行下去,每一次事件处理结束后等待下一个事件的发生;没有要处理...
  • Qt事件和事件循环

    千次阅读 2017-11-14 11:48:14
    Qt事件和事件循环 在处理QT循环事件的时候遇到了问题,查了半天资料都没弄明白问题出在哪,后来找大牛同事问了一下,同事就给我写了QCoreApplication::processEvent()这个函数,好啦,终于搞定了,这里小记...
  • qemu利用glib接口实现主线程的事件循环,如果没有glib接口的使用经验,理解起来比较困难,本文试图用glib接口的demo,分解qemu主事件循环
  • Node的“事件循环”(Event Loop)是它能够处理大并发、高吞吐量的核心。这是最神奇的地方,据此Node.js基本上可以理解成“单线程”,同时还允许在后台处理任意的操作。这篇文章将阐明事件循环是如何工作的,你也...
  • js事件循环机制

    万次阅读 多人点赞 2018-08-14 17:39:13
     从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件循环。在此之前需要了解JS单线程,及由此产生的同步...
  • Qt事件处理,事件循环

    千次阅读 2017-10-30 14:45:34
    Qt事件处理的基本方法
  • 面试题:说说事件循环机制(满分答案来了)

    千次阅读 多人点赞 2020-03-09 08:45:00
    答题大纲先说基本知识点,宏任务、微任务有哪些说事件循环机制过程,边说边画图出来说async/await执行顺序注意,可以把 chrome 的优化,做法其实是违反了规范的,V8 团队的PR...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 514,740
精华内容 205,896
关键字:

事件循环