美文网首页
浏览器/nodeJS中的EventLoop

浏览器/nodeJS中的EventLoop

作者: williamslau | 来源:发表于2018-03-27 16:41 被阅读0次

    大家都知道JS是一门单线程语言,也就意味着JS无法进行多线程,但是JS中异步的概念完全可以模拟多线程,而且效果差不到哪去

    要完全理解异步,就需要了解 JS 的运行核心——事件循环(event loop)

    但是在浏览器中运行JS和在nodeJS中运行还有一些差别,接下来我们就来看看这些差别在哪里

    首先是浏览器中的EventLoop
    在说浏览器EventLoop之前我们要先看看浏览器模型

    浏览器模型.png
    • 用户界面 包括地址栏,书签栏等那些东西
    • 浏览器引擎 在用户界面,呈现引擎之间传送指令
    • 渲染引擎 也被称为呈现引擎
    • 请求(网络) 用于网络调用,比如HTTP请求
    • JS解释器 用于解析和执行JS代码
    • UI(用户界面后端) 用于绘制样式 和JS共用一个线程
    • 数据存储 浏览器需要保存的一些数据 不如Cookie

    JS和css公用一个线程是因为浏览器不会同时渲染JS和css,一般都会先渲染css再执行JS

    浏览器中的微任务
      then
      messageChannel
      mutationObersve

    浏览器中的宏任务
      setTimeout
      setInterval

    代码调用先进堆栈,堆栈是代码的总执行站,堆栈整个执行的过程中会先将微任务,宏任务放到相应的队列中,事件提出来等待触发,等到总执行站中的代码空了,会先看微任务队列中有没有,如果有就会放到总执行站中执行,然后在看宏任务队列中有没有。

    浏览器EventLoop.png
    setTimeout(function () {
        console.log('setTimeout')
    })
    Promise.resolve().then(function () {
          console.log('promise')
    });
    console.log('堆栈');
    
    // 执行结果:
    // 堆栈
    // promise
    // setTimeout
    

    如果把微任务队列放到堆栈中执行的时候又发现了宏任务,会顺便吧微任务中的宏任务一起执行了

    setTimeout(function () {
        console.log('setTimeout1')
        Promise.resolve().then(function () {
            console.log('promise')
        });
    })
    setTimeout(function () {
        console.log('setTimeout2');
    });
    
    // 执行结果:
    // setTimeout1
    // promise
    // setTimeout2
    

    然后是nodeJS运行环境中的EventLoop
    nodeJS中的宏任务和微任务在浏览器的基础上有新增了几个
    微任务
      then
      nextTick
      messageChannel
      mutationObersve

    宏任务
      setTimeout
      setInterval
      setImmediate、

    node的调用顺序中会比浏览器中的复杂一些

    nodeJS EventLoop.png

    看图虽然复杂,但是我们只需要关心timers计时器阶段,poll轮询阶段,check检查阶段(setImmediate回掉),clons关闭阶段以及微任务队列即可,因为处理网络,内部调用与咱们的宏任务和微任务的执行没有太大的关系
    和浏览器运行环境不同的是微任务只会在阶段转化的时候才会调用,就是close关闭阶段后再执行下一阶段的时候

    process.nextTick(function () {
        console.log('nextTick')
    });
    setImmediate(function () {
        console.log('setImmediate')
    });
    
    // 执行结果:
    // nextTick
    // setImmediate
    

    如果宏任务执行的时候又发现微任务了,不会和浏览器一样顺便执行了,而是会将微任务再放到微任务队列中,等待整个阶段结束后,下一个阶段开始的时候先执行完微任务队列中的微任务

    setTimeout(function () {
        console.log('setTimeout1')
        Promise.resolve().then(function () {
            console.log('promise')
        });
    })
    setTimeout(function () {
        console.log('setTimeout2');
    });
    
    // 执行结果:
    // setTimeout1
    // promise
    // setTimeout2
    

    反之亦然

    process.nextTick(function () {
        console.log(1)
        setImmediate(function () {
            console.log(2);
        })
    })
    setImmediate(function () {
        console.log(3);
        process.nextTick(function () {
            console.log(4)
        })
    })
    
    // 执行结果:
    // 1
    // 3
    // 2
    // 4
    

    timeout immediate 两个谁先执行不一定 取决于node的执行时间

    setTimeout(function () {
        console.log('setTimeout');
    })
    setImmediate(function () {
        console.log('setImmediate')
    });
    
    // 执行结果:
    // setTimeout
    // setImmediate
    // 或者:
    // setImmediate
    // setTimeout
    

    但是加上i/o文件操作以后就会先执行setImmediate,因为setImmediate在i/o文件操作后面的那个阶段执行,执行完setImmediate会在下一个阶段的时候再执行setTimeout (timers 计时器执行阶段)

    let fs = require('fs');
    fs.readFile('./1.txt', function () {
        console.log('fs');
        setTimeout(function () {
            console.log('setTimeout');
        })
        setImmediate(function () {
            console.log('setImmediate')
        })
    });
    
    // 执行结果
    // fs
    // setImmediate
    // setTimeout
    

    相关文章

      网友评论

          本文标题:浏览器/nodeJS中的EventLoop

          本文链接:https://www.haomeiwen.com/subject/ilajcftx.html