事件循环机制

发布时间:2024年01月22日

初认识:

javaScript事件循环进制是一个用于处理异步事件的回调函数的机制。是JavaScript实现异步编程的核心。

  1. 用于管理任务队列和调用栈,以及在适当的时候执行回调函数。
  2. 可以监听消息队列中的事件,并根据事件处理的优先级顺序来依次执行相应的回调函数。

事件循环机制允许JavaScript在等待某些任务(异步任务:通常需要一定时间才能完成)完成的同时,可以执行其他任务,避免了阻塞,解决了单线程并发性的问题。

JavaScript事件循环是为了解决JavaScript的单线程并发性问题,实现单线程非阻塞。javaScript是一个单线程,同一时间只能执行一件事情。这样单一的特性会导致JavaScript在处理长时间运行的操作的时候出现阻塞,阻塞主要还是异步任务的问题。

基础:

  • 同步任务:只有前一个任务执行完成之后,才能执行后一个任务。Promise()的参数,在async函数中的await右边的代码都是作为同步代码执行的
  • 异步任务:在执行这个任务的同时,可以执行别的任务,异步任务都是耗时的。不进入主线程,进入“任务队列"的任务,当主线程的任务运行完了之后,才会从“任务队列”取出,放入主线程执行。

微任务(microtask):

  • promise,只有Promise.then()的回调函数才会放入微任务中
  • async (async函数中的await右边的代码是同步代码)
  • await
  • process.nextTick(node)
  • mutationObserve

宏任务(macrotask):

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

执行顺序(优先级):同步任务 -> 微任务 -> 宏任务。不断重复这个过程。(JS引擎指向代码是从上往下执行)

宏任务中的微任务执行顺序:宏任务执行完之后,微任务就一个接一个的执行。

在一轮循环中,会先执行一个宏任务,然后把微任务队列中的所有任务全部执行。然后在下一轮循环中,继续这样执行。

事件循环:

参考信息:并发模型与事件循环 - JavaScript | MDN (mozilla.org)

可视化展示:

事件循环组成:

参考资料:事件循环机制-执行栈、调用栈 - 渣渣逆天 - 博客园 (cnblogs.com)

一看就懂的事件循环机制(event loop) - 掘金 (juejin.cn)

调用栈(Stack):

调用栈用于存储在代码执行阶段创建的所有执行上下文。

js有且只有一个调用栈,因为js在某个时刻只能做一件事。

调用栈的目的是为了帮助 JS编译器 用于追踪函数被调用的顺序的一种机制。

演示:

const second = () => {
  console.log('Hello there!')
}
const first = () => {
  console.log('Hi there!')
  second();
  console.log('The End')
}
first()

堆(heap):

保存的地址。

对象被分配在堆中,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语。

队列(Queue):

保存事件对应的回调函数,在事件执行时,被弹出。

一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。

在 事件循环 期间的某个时刻,会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧。

函数的处理会一直进行到栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。

Web API:

浏览器提供了多种异步的Web API,如DOM,times(计时器),AJAX等。

当我们调用一个 Web API 时,如 setTimeout,setTimeout函数会被 push 调用栈顶然后执行,但是 setTimeout 的回调函数不会立即被 push 到调用栈顶,而是起一个计时器任务。当这个计时器结束时,该回调函数会被塞到任务队列(CallBack Queue)中。这个队列中的回调函数的调用就是由事件循环机制来控制的。

理解: 调用任务时,并不会马上进入任务队列,而是先调用web提供的api,等待执行的结果才放进去任务队列

事件循环效果展示视频:一个动画搞清前端js事件循环,面试再也不慌了_哔哩哔哩_bilibili

?

举栗子🌰:

参考视频:面试官:说一说事件循环?_哔哩哔哩_bilibili

栗子1:

console.log('script start');
async function async1(){// 微任务
    await async2();// 同步任务
    console.log('async1 end');
}
async function async2(){// 同步任务
    console.log('async2 end');
}
async1()
setTimeout(function(){// 宏任务
    console.log('setTimeout1');// 同步任务
    new Promise(res => res())
    .then(()=>{
        console.log('promise in timer1');
    })
    .then(()=>{
        console.log('promise in timer2');
    })
},0)
setTimeout(function(){// 宏任务
    console.log('setTimeout2');
},0)
new Promise(resolve => {// 微任务
    console.log('promise1');// 同步任务
    resolve()
    console.log('promise2');
})
    .then(function(){
        console.log('promise3');
    })
    .then(function(){
        console.log('promise4');
    })
console.log('script end');
// 结果
script start
async2 end
promise1  
promise2  
script end
async1 end
promise3  
promise4  
setTimeout1
promise in timer1
promise in timer2
setTimeout2

栗子2:

new Promise(resolve=>{
    console.log('promise1');
    resolve()
    console.log('promise2');
})
    .then(function(){
        console.log('promise3');
        setTimeout(function(){
            console.log('setTimeout3');
        },0)
    })
    .then(function(){
        console.log('promise4');
        setTimeout(function(){
            console.log('setTimeout4');
        },0)
    })
    .then(function(){
        console.log('promise5');
        setTimeout(function(){
            console.log('setTimeout5');
        },0)
    })
// 结果
promise1
promise2
promise3
promise4
promise5
setTimeout3
setTimeout4
setTimeout5

文章来源:https://blog.csdn.net/bu_xiang_tutou/article/details/135740238
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。