这篇文章主要讲解了“JavaScript事件循环怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript事件循环怎么使用”吧!
JavaScript事件循环是一种机制,用于处理异步事件和回调函数。它是JavaScript运行时环境的一部分,负责管理事件队列和调用栈。
事件循环的基本原理是事件循环的核心是一个事件队列,所有的事件都被放入这个队列中,然后按照顺序依次执行。如果队列为空,JavaScript会等待新的任务加入队列。当JavaScript代码执行时,所有同步任务都会被立即执行,而异步任务则会被放入事件队列中。
当所有同步任务执行完毕后,事件循环会从事件队列中取出一个任务,并将其放入调用栈中执行。当该任务执行完毕后,事件循环会再次从事件队列中取出下一个任务,并重复这个过程。
一、事件循环的执行过程
1、执行同步代码,直到遇到第一个异步事件(如setTimeout、setInterval、Promise等)。
2、将异步事件放入事件队列中,并继续执行同步代码。
3、当所有同步代码执行完毕后,JavaScript引擎会检查事件队列中是否有事件需要执行。
4、如果事件队列中有事件需要执行,JavaScript引擎会将第一个事件取出来,并执行对应的回调函数。
5、执行完回调函数后,JavaScript引擎会再次检查事件队列中是否有事件需要执行,如果有则重复步骤4,否则继续等待新的事件加入事件队列。
需要注意的是,事件循环是单线程的,也就是说JavaScript引擎在同一时间只能执行一个任务。
因此,如果一个任务执行时间过长,会阻塞事件循环,导致其他任务无法执行。为了避免这种情况,可以将长时间的任务拆分成多个小任务,使用setTimeout或setInterval分批执行。
另外,Promise也是基于事件循环的机制实现的。当Promise状态发生改变时,会将对应的回调函数放入微任务队列中,等待当前任务执行完毕后立即执行。因此,Promise的回调函数总是在当前任务执行完毕后立即执行,而不会被放入事件队列中等待执行。
二、事件循环进阶用法
1、
Promise:Promise是一种异步编程的解决方案,它可以将异步操作转化为同步操作的形式,使得代码更加简洁易懂。Promise的基本原理是将异步操作封装成一个Promise对象,通过then方法来处理异步操作的结果。
2、
async/await:async/await是ES2017引入的一种异步编程的解决方案,它可以让异步代码看起来像同步代码,使得代码更加易读易懂。async/await的基本原理是使用async关键字定义一个异步函数,然后在函数内部使用await关键字来等待异步操作的结果。
3、定时器:JavaScript中有两种定时器,分别是setTimeout和setInterval。setTimeout用于在指定的时间后执行一次任务,而setInterval则用于每隔一段时间执行一次任务。这两种定时器都是异步任务,会被放入任务队列中等待执行。
三、JavaScript任务类型
JavaScript中的任务按照不同纬度进行区分主要分为同步任务和异步任务、宏任务和微任务。
3.1 同步任务&异步任务
1、 同步任务按照代码顺序执行的任务。
2、 异步任务是在将来某个时间点执行的任务。异步任务通常是由事件触发器(如鼠标点击、网络请求等)生成的。 当一个异步任务被触发时,它会被放入任务队列中等待执行。
JavaScript事件循环的执行顺序可以用以下伪代码表示:
while (queue.waitForMessage()) { queue.processNextMessage(); }
在这个伪代码中,
queue.waitForMessage()表示等待队列中有待执行的任务,
queue.processNextMessage()表示取出队列中的下一个任务并执行。
需要注意的是,JavaScript事件循环的执行顺序是不可中断的。这意味着当一个任务正在执行时,其他任务必须等待,直到当前任务完成。因此,如果一个任务执行时间过长,会导致其他任务无法及时执行,从而影响应用程序的性能和响应速度。
3.2 宏任务&微任务
1、宏任务包括setTimeout、setInterval、I/O操作等。
2、微任务包括Promise、MutationObserver等。
当事件循环从事件队列中取出一个任务时,它会先执行所有微任务,然后再执行一个宏任务。这个过程会一直重复,直到事件队列中的所有任务都被执行完毕。
下面是一个例子:
console.log('1'); setTimeout(function() { console.log('2'); Promise.resolve().then(function() { console.log('3'); }); }, 0); Promise.resolve().then(function() { console.log('4'); }); console.log('5'); // 输出结果为: 1 5 4 2 3
执行顺序:
执行第一个
console.log,输出1。
执行
setTimeout,将其回调函数放入宏任务队列中。
执行
Promise.resolve().then,将其回调函数放入微任务队列中。
执行第二个
console.log,输出5。
当前任务执行结束,执行微任务队列中的所有任务,输出4。
执行宏任务队列中的第一个任务,即
setTimeout的回调函数,输出2。
执行
Promise.resolve().then的回调函数,输出3。
需要注意的是,微任务和宏任务的执行顺序是固定的,即先执行所有微任务,再执行宏任务。因此,如果在一个宏任务中产生了新的微任务,那么这些微任务会在下一个宏任务执行之前执行。
下面是一个例子:
console.log('1'); setTimeout(function() { console.log('2'); Promise.resolve().then(function() { console.log('3'); }); }, 0); Promise.resolve().then(function() { console.log('4'); setTimeout(function() { console.log('5'); }, 0); }); console.log('6'); // 输出结果为:1 6 4 2 3 5
执行顺序:
执行第一个
console.log,输出1。
执行
setTimeout,将其回调函数放入宏任务队列中。
执行
Promise.resolve().then,将其回调函数放入微任务队列中。
执行第三个
console.log,输出6。
当前任务执行结束,执行微任务队列中的所有任务,输出4。
执行宏任务队列中的第一个任务,即
setTimeout的回调函数,输出2。
执行
Promise.resolve().then的回调函数,将setTimeout的回调函数放入宏任务队列中。
当前任务执行结束,执行微任务队列中的所有任务,输出3。
执行宏任务队列中的第一个任务,即
setTimeout的回调函数,输出5。