事件循环EventLoop

157 阅读4分钟

笔者大三,前端,期待实习机会,以下是在自己准备面试过程中的一点心得。

如果有错误请多指出。

在js中,程序的一部分现在运行,而另一部分在将来运行,现在和将来之间有段时间间隙。

如何处理这段间隙,如何处理程序的当下运行与未来运行部分是异步编程的核心。

JavaScript引擎运行于宿主环境中,这里简单理解为浏览器,js主线程负责执行代码,浏览器中还有很多线程负责js代码执行调度。

浏览器提供了一种机制来处理代码中多个代码块的执行,且执行每块时调用JavaScript引擎,(js是由JavaScript引擎执行的),这种机制叫事件循环。

接下来需要分清楚两个队列

事件循环队列:存放宏任务

任务队列(job queue):存放微任务

接下来借用别人的宏微任务定义:

宏任务(macrotask):

script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)

微任务(microtask):

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

看过不少八股博客文,里面都在纠结宏任务微任务优先级之分,笔者自认为没有必要,是片面的理解,想深入理解就不要纠结优先级。

然后对JavaScript引擎执行补充一些知识:

JavaScript引擎执行,维护一个唯一主执行栈,主执行栈真正执行Js代码**。**

下面我对《你所不知道的JavaScript》中关于事件循环机制表述的代码做一些添加,然后呈现给读者。

var eventQueue = [];//事件循环队列
var jobQueue = [];//任务队列
let event, job;
while (true) {    //while 中的一轮代码叫做一次tick
//Event Loop(事件循环)中,每一次循环称为 tick    
    if (eventQueue.length > 0) {        
        event = eventQueue.shift();        
        try {            
            event(); //从事件队列拿到主执行栈执行             
            if (event在执行过程中产生宏任务) {                
            eventQueue.push(新的宏任务)           
            }            
            if (event在执行过程中产生微任务) {   
             jobQueue.push(新的微任务)           
            }        
        } catch (err) {   
             reportErr(err); //报告错误        
        }    
    }    
    while (jobQueue.length > 0) {        
        job = jobQueue.shift();        
        try {            
            job(); //从任务队列拿到主执行栈执行           
            if (job在执行过程中产生宏任务) {                
                eventQueue.push(新的宏任务)            
            }            
            if (job在执行过程中产生微任务) {                
                jobQueue.push(新的微任务)            
            }        
        } catch (err) {            
            reportErr(err); //报告错误        
        }       
    }
}

请仔细阅读代码,接下来是对代码内容的总结说明:

while 中的一轮代码叫做一次tick就是Event Loop(事件循环)中的每一次循环。

宏任务(macrotask)中有个script(整体代码),笔者之前不理解,但现在明白并且说说自己的理解**:
**JavaScript引擎执行栈 选择 任务队列队头 的宏任务就是是

script

**整体代码
**所以同步代码一定比异步代码先执行(这句话我目前坚信不疑)。

事件循环执行顺序 宏任务一个-微任务一堆-宏任务一个-微任务一堆

宏微任务执行时也会继续产生一些宏微任务,

譬如

async function fn() {    
let a = await Promise.resolve('async1');
    console.log(a);   
    setTimeout(function() { console.log("wuhu in  async") }, 0);
    let b = await Promise.resolve('async2');
    console.log(b);   
    let c = await Promise.resolve('async3');
    console.log(c);
}

在执行微任务时产生了一个 setTimeOut宏任务,那就把这个宏任务入事件循环队列末尾即可。

笔者自己写了个测试demo,给大家练习

async function fn() {    
    let a = await Promise.resolve('async1');
    console.log(a);    
    setTimeout(function() { console.log("wuhu in  async") }, 0);    
    let b = await Promise.resolve('async2') ;
    console.log(b); 
    let c = await Promise.resolve('async3')
    console.log(c);
}
    console.log("同步");
    console.log("同步");
    console.log("同步");    
    new Promise(function(resolve, reject) {    
        console.log("wuhu in  promise") //Promise构造器里面的代码会立即执行    
        resolve("promise fulfiiled");})
        .then((data) => { console.log(data); })
    console.log("同步");
    console.log("同步");
    console.log("同步");
    console.log("同步");
    fn();
    console.log("同步");
    setTimeout(function() { console.log("wuhu1") }, 0);
    console.log("同步");
    console.log("同步");
    setTimeout(function() { console.log("wuhu2") }, 0)
    console.log("同步");
    console.log("同步");
    console.log("同步");
// VM63:11 同步
// VM63:12 同步
// VM63:13 同步
// VM63:15 wuhu in  promise
// VM63:19 同步
// VM63:20 同步
// VM63:21 同步
// VM63:22 同步
// VM63:24 同步
// VM63:26 同步
// VM63:27 同步
// VM63:29 同步
// VM63:30 同步
// VM63:31 同步
// VM63:17 promise fulfiiled
// VM63:3 async1
// VM63:6 async2
// VM63:8 async3
// VM63:25 wuhu1
// VM63:28 wuhu2
// VM63:4 wuhu in  async