JavaScript学习笔记:任务执行机制及事件循环

任务执行机制

javascript程序是单线程运行的,意味着执行多个任务需要将它们排队。
根据应用场景的不同,任务被分为同步任务与异步任务。
同步任务即是立即执行的代码,由JavaScript线程执行。
异步任务是由宿主环境开辟新的线程来执行,在任务完成后通知主线程。异步任务是基于回调实现的。

Javascript的单线程与宿主环境的多线程

所有的JavaScript实现都是单线程的,解析与执行JS代码都是在这个线程中进行,称之为主线程。
但这不意味着JS引擎和其宿主环境如浏览器,也是单线程的。
JS引擎是多线程的,只不过是同一个程序的脚本被限定在一个线程中运行罢了。
浏览器都是多线程的,浏览器的DOM操作、网络操作、定时器等都有自己的线程,它们与主线程是并行的,可以进行通信的。
浏览器定义了许多API用于实现各种异步的耗时操作,比如内置fetch()函数用于网络操作,它接收一个回调函数,并将网络操作的结果作为参数传入回调函数。
它会开辟新的线程来进行网络操作,在网络操作完成后,该线程与主线程通信,告诉主线程任务执行完毕,主线程会在空闲的时候执行与异步操作对应的回调函数。

同步任务与异步任务以及任务队列

定义与赋值语句、循环语句、console.log()等都是同步任务。
而document.querySelector(), setTimeout(), EventTarget.addEventListener(), Promise等则是异步任务。

同步任务进入主线程执行栈,直接执行。
异步的任务开辟新的线程,与主线程并行,同时异步任务对应的回调函数被添加到Event Table。
在异步任务执行完毕后,对应的函数被从Event Table 转移到 Event Queue。
这个Event Queue即是任务队列,虽然它应该被翻译为事件队列。
在主线程的同步任务执行完毕后,异步的任务对应的函数被从任务队列中取出,进入主线程执行。

微任务与宏任务以及事件循环

任务被划分为微任务与宏任务,它们有着不同的任务队列,宏任务比微任务更先执行。
任务队列里的任务是循环执行的,在一次循环中,宏任务只能执行一个,而微任务则执行队列中的全部。
这个执行过程即是事件循环。
常见的宏任务有setTimeout(), setInterval(), 网络操作如Ajax和fetch(), 以及脚本代码自身的所有同步代码也作为一个宏任务。
异步任务必然有回调函数,在异步任务执行完毕后,该函数会被添加到任务队列,等待主线程空闲后再执行该函数。

代码示例

console.log(1);
setTimeout(function() {
  console.log(2);
  new Promise(function(resolve) {
      console.log('3');
      resolve();
  }).then(function() {
      console.log('4')
  });
});

new Promise(function(resolve) {
    console.log('5');
    resolve();
}).then(function() {
    console.log('6')
})

首先整个程序代码是同步执行的,是宏任务,第一轮事件循环,先执行这个宏任务。
打印1;
然后setTimeout()作为宏执行,其回调函数作被加入宏任务的事件队列;
然后new Promise()执行,作为构造函数的参数的回调函数被同步执行, 打印5,在resolve()作为微任务被执行后,Promise对象的then()方法接收的函数被添加到微任务的事件队列。
此时第一轮事件循环,开始执行微任务的回调。打印6,第一轮事件循环结束。
第二轮开始,先执行宏任务,打印2,打印3,执行resolve(),将then()方法的函数参数添加到微任务的事件队列。
然后执行微任务回调,打印4。
程序到此执行完毕。

posted @ 2023-06-09 23:52  钰琪  阅读(45)  评论(0编辑  收藏  举报