返回

事件循环是一场奇妙的旅程——用实例彻底搞定Javascript事件循环

前端

踏上Javascript事件循环的奇妙旅程

Javascript,一门单线程、非阻塞的脚本语言,总能带给我们惊喜。它只有一个主线程来执行所有任务,所有的任务必须排队来执行。那么,问题来了:浏览器在执行耗时较长的任务时,为什么还能响应用户界面的交互呢?非阻塞又从何说起呢?

带着这些疑问,让我们踏上Javascript事件循环的奇妙旅程,一探究竟。

单线程与非阻塞:矛盾背后的协奏曲

Javascript的主线程只有一个,就像一个乐团的指挥家,它一次只能执行一项任务,就像指挥家一次只能指挥一个乐队。而我们编写的Javascript代码就像乐谱,指挥家根据乐谱来指挥乐队演奏出美妙的音乐。

但是,Javascript中的任务多种多样,有的任务很快,有的任务很慢。如果指挥家一直等到慢任务执行完毕再执行下一个任务,那么乐队就会停滞不前。为了解决这个问题,Javascript采用了非阻塞的策略。

非阻塞意味着指挥家不会等待慢任务执行完毕,而是先执行其他快任务。等慢任务执行完毕后,指挥家会再回来执行它。这样一来,整个乐团就不会因为慢任务而停滞不前。

同步与异步:任务的两种姿态

任务在Javascript中的执行方式主要有两种:同步和异步。

同步任务就像一个急性子,它必须立即执行完毕,指挥家必须等到它执行完毕再执行下一个任务。异步任务则是一个慢性子,它可以慢慢执行,指挥家可以先执行其他任务,等它执行完毕后再来执行它。

常见的同步任务包括:变量声明、函数调用、运算符操作等。常见的异步任务包括:setTimeout、setInterval、AJAX请求等。

回调:任务之间的对话

异步任务在执行完毕后,需要通知主线程已经执行完毕,以便主线程可以继续执行后续的任务。这个通知的过程就是回调。

回调就像一个信使,它将异步任务执行完毕的消息传递给主线程。主线程收到消息后,就会知道异步任务已经执行完毕,然后继续执行后续的任务。

宏任务与微任务:任务的优先级

Javascript将任务分成了宏任务和微任务两种。宏任务包括:script脚本、setTimeout、setInterval、AJAX请求等。微任务包括:promise、mutation observer等。

宏任务和微任务都有自己的任务队列。当主线程执行完一个宏任务后,会先检查微任务队列中是否有微任务,如果有,就会先执行微任务队列中的所有微任务,然后再执行下一个宏任务。

事件循环:任务的舞台

Javascript事件循环就像一个舞台,它不断地从任务队列中取出任务并执行它们。这个过程会一直持续下去,直到任务队列为空。

事件循环的具体过程如下:

  1. 主线程从宏任务队列中取出一个宏任务并执行它。
  2. 在执行宏任务的过程中,如果遇到了异步任务,就会将异步任务添加到微任务队列中。
  3. 当主线程执行完一个宏任务后,就会检查微任务队列中是否有微任务,如果有,就会先执行微任务队列中的所有微任务,然后再执行下一个宏任务。
  4. 重复步骤1-3,直到任务队列为空。

实例代码:揭秘事件循环

// 同步任务
console.log('同步任务1');

// 异步任务
setTimeout(() => {
  console.log('异步任务1');
}, 0);

// 同步任务
console.log('同步任务2');

// 微任务
Promise.resolve().then(() => {
  console.log('微任务1');
});

// 宏任务
setTimeout(() => {
  console.log('宏任务2');
}, 0);

// 微任务
Promise.resolve().then(() => {
  console.log('微任务2');
});

输出结果:

同步任务1
同步任务2
异步任务1
微任务1
微任务2
宏任务2

从输出结果中,我们可以看到:

  1. 同步任务先执行,异步任务后执行。
  2. 微任务在宏任务之前执行。
  3. 微任务按照先进先出的顺序执行。

结束语

Javascript事件循环是一个复杂的概念,但只要我们理解了它的基本原理,就可以轻松掌握它的运作机制。通过本文的讲解,相信大家对Javascript事件循环有了一个更深入的了解。