javascript中的Event Loop详解

2018-01-20  本文已影响0人  lihuanji

首先来一段代码开篇

    console.log(1);
    
    setTimeout(function() {
      console.log(2);
    });
    
    function fn() {
        console.log(3);
        setTimeout(function() {
          console.log(4);
        }, 2000);
    }
    
    new Promise(function(resolve, reject){
        console.log(5);
        resolve();
        console.log(6);
    }).then(function() {
       console.log(7);
    })
    
    fn();
    console.log(8);

思考一下,能给出准确的输出顺序吗?

下面一步步的了解,最后看看这块代码怎么去执行的。

1.进程,单线程与多线程

进程: 运行的程序就是一个进程,比如你正在运行的浏览器,它会有一个进程。

线程: 程序中独立运行的代码段。

一个进程由单个或多个线程组成,线程是负责执行代码的。

学过JS的想必都知道JS是单线程的,那么既然有单线程就有多线程,下面首先看看单线程与多线程的区别。

2. Event Loop(浏览器)

js既然是单线程,那么肯定是排队执行代码,那么怎么去排这个队,就是Event Loop。虽然JS是单线程,但浏览器不是单线程。浏览器中分为以下几个线程:

其中JS线程和UI线程相互互斥,也就是说,当UI线程在渲染的时候,JS线程会挂起,等待UI线程完成,再执行JS线程

整个过程,执行栈,读取事件队列就是Event Loop

3. Node Event Loop

Nodejs是通过V8引擎去解析的,解析后的代码会去调用node提供的api执行,这些API由libuv这个库去分配线程执行,最后异步返回给V8引擎。

在Node中提供了2个方法和我们的执行队列有关

把方法放入执行栈的底部,并不放入宏任务和微任务

   cosnole.log(1);
   
   process.nextTick(function(){
       console.log(2);
   });
   
   new Promise(function() {
       console.log(3);
   }).then(function() {
       console.log(4);
   })
   
   console.log(5);

因为nextTick是放入了执行栈的底部,那么会优先于Promise的then方法,故输出为1 3 5 2 4

把方法放入宏任务的队列中去,但有一个奇怪的事发生,看下面代码:

   setImmediate(function() {
       console,log(1);
   });
   
   setTimeout(function() {
       console.log(2);
   }, 0);

大家可以试试把代码多次执行,发现输出顺序不一定,他们都是放入了宏任务中,但在node文档中,setImmediate总是排在setTimeout前面,但是在实际中确不一定,不知道是不是一个bug。

4. 讲讲setTimeout, setInterval

    setTimeout(function(){
        console.log(1);
    }, 2000);
    
    task();

假设task函数执行需要5秒钟,那么打印1需要在5秒之后再打印,task占用了当前执行栈,要等执行栈执行完毕后再去读取微任务,等微任务完成,这个时候才会去读取宏任务里面的setTimeout回调函数执行。setInterval同理,例如每3秒放入宏任务,也要等到执行栈的完成。

    setTimeout(function() {
        console.log(1);
    },0);

但是根据标准这个时候最低是4毫秒,即便现在执行栈已经完成。0是不成立的。写0浏览器为默认为最低毫秒数。

5. 回到开篇的代码

现在再回到上面的代码,有答案了吗?

    // 非异步api,立即执行
    console.log(1);
    
    // 放入全局宏任务
    setTimeout(function() {
      console.log(2);
    });
    
    // 声明函数,但暂时未调用,不会立马形成执行栈
    function fn() {
         // 调用fn时立即执行
        console.log(3);
        
        // 放入当前fn执行栈宏任务
        setTimeout(function() {
          console.log(4);
        }, 2000);
    }
    
    new Promise(function(resolve, reject){
        // task任务立即执行
        console.log(5);
        resolve();
        console.log(6);
    }).then(function() {
       // then方法放入微任务
       console.log(7);
    })
    
    // 调用fn进入下个执行栈
    fn();
    
    // fn执行栈完成执行
    console.log(8);

答案就是 1 5 6 3 8 7 2 4

上一篇下一篇

猜你喜欢

热点阅读