美文网首页
浅谈JavaScript的Event-Loop

浅谈JavaScript的Event-Loop

作者: 沐源山 | 来源:发表于2020-01-17 10:35 被阅读0次

    在之前的一篇文章中我们解释了一下为什么JavaScript要设计成单线程以及这门语言的任务队列的概念,这也帮助了我们简单了解了这门语言的运行机制,那么今天我们就谈谈任务队列相关的概念!

    事件和回调函数

    任务队列其实是事件的一个队列,也可以理解为消息队列,当IO设备完成一个任务的时候,就会在任务队列中添加一个事件,用来表示当前任务已经执行完了,可以进入执行栈(也就是之前讲过的主线程队列)了,主线程读取任务队列也就是读取有哪些事件!

    任务队列中的事件除了IO设备之外,还有用户点击、键盘事件等,只要指定过回调函数,这些事件发生时就会进入任务队列,然后等待主线程读取。

    回调函数(callback)其实就是被主线程挂起来的代码,主线程执行异步任务,其实就是执行回调函数!

    任务队列其实是一个先进先出的数据结构,也就是说排在前面的事件会被优先读取,当主线程的代码执行完,执行栈被清空以后,就会立即执行任务队列中排在最前面的事件,但是由于定时器的功能,因此,主线程需要检查一下执行时间,某些事件只有等到规定的时间,才可以返回主线程!

    Event Loop

    主线程从任务队列读取事件这个过程是循环不断的,因此整个过程又被称为Event Loop(事件循环)

    setTimeout(() => {
      console.log('timeout1');
    }, 0)
    console.log(1);
    setTimeout(() => {
      console.log('timeout2');
    }, 0)
    console.log(2);
    // 1 2 timeout1 timeout2
    

    上面代码中有两个定时器,定时器也会放在任务队列中,因此我们常说在js中定时器可以模拟异步,其实是js默认会把定时器放在任务队列,前面我们讲过,js先会执行主线程的代码,称为执行栈,当执行栈的代码执行结束,执行栈清空之后才会执行任务队列中的代码, 因此上面的代码不会因为定时器在前面,而先执行定时器,当定时器的间隔时间一致时,按照添加顺序,先进任务队列则先执行!

    NodeJs的Event Loop

    NodeJs也是单线程的Event Loop,但是它区别于浏览器的运行环境;
    在Nodejs中提供了process.nextTice()和setImmediate()两个与任务队列有关的方法;

    process.nextTice()就是在当前执行栈尾部添加任务,也就是任务队列(所有的异步任务)开始之前;

    console.log(1);
    setTimeout(function timeout() {
      console.log('timeout1');
    }, 0);
    
    process.nextTick(function() {
      console.log(3);
      process.nextTick(function(){
        console.log(4);
      });
    });
    
    setTimeout(function timeout() {
      console.log('timeout2');
    }, 0);
    
    console.log(2);
    
    // 1 2 3 4 timeout1 timeout2
    

    setImmediate()会在每一次Event Loop结束执行,或者说下一次Event Loop执行之前执行

    console.log('start');
    setTimeout(function() {
        console.log('timeout1');
    }, 0);
    setImmediate(function (){
        setImmediate(function() {
            console.log(1);
            setTimeout(function() {
                console.log('timeout3');
            }, 0);
            setImmediate(function(){
                console.log(2);
            });
        });
        setTimeout(function() {
            console.log('timeout2');
        }, 0);
    });
    process.nextTick(function() {
        console.log('nextTick1');
        process.nextTick(function() {
            console.log('nextTick2')
        })
    })
    console.log('end');
    // start end nextTick1 nextTick2 timeout1 1 timeout2 2 timeout3
    

    上面代码前三个输出结果在没有其他干扰,就目前代码,不用质疑,主线程肯定先执行,接下来为nextTick,因为它会被放在所有异步执行之前,不论是否嵌套(不包括嵌套在其他异步函数中),timeout1在主线程程中被添加到任务队列,不论它是否在start之后还是end之前,它是区别于其他函数的唯一一个在任务队列顶端的函数,而setImmediate总会在一个Event Loop之后执行!

    下篇我们我们聊聊任务队列中的微任务和宏任务!

    相关文章

      网友评论

          本文标题:浅谈JavaScript的Event-Loop

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