和我一起完全理解javascript中的宏任务与微任务

在了解 宏任务 微任务 之前我们必须要先弄清 javascript 中的 执行机制 这个概念,弄清楚了这个概念,宏任务 微任务 的理解也就 so easy 了~~

javascript 中的执行机制

1.关于javascript

javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的。

1.1 JS为什么是单线程的?

答:JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。

比如:假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

1.2 JS为什么需要异步?

如果JS中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被 阻塞。 对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验。

1.3 JS单线程又是如何实现异步的呢?

既然JS是单线程的,只能在一条线程上执行,又是如何实现的异步呢?
是通过的事件循环(event loop),理解了event loop机制,就理解了JS的执行机制。

2.javascript的同步和异步

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

1.同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
2.异步任务指的是,不进入主线程、而进入" 任务队列 “(taskqueue)的任务,只有” 任务队列 "通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

和我一起完全理解javascript中的宏任务与微任务_第1张图片

解释上面的图:

  1. 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入 Event Table(事件列表) 并注册函数。
  2. Event Table(事件列表) 中指定的事情完成时,会将这个函数移入 Event Queue(事件队列)
  3. 主线程内的任务执行完毕为空,会去 Event Queue(事件队列) 读取对应的函数,进入主线程执行
  4. 上述过程会不断重复,也就是常说的Event Loop(事件循环)
  5. 我们不禁要问了,那怎么知道主线程执行栈为空啊?
    js引擎存在 monitoring process 监控进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去 Event Queue(事件队列) 那里检查是否有等待被调用的函数。
2.1 任务队列
  1. “任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列”,就是读取里面有哪些事件。
  2. “任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列”,等待主线程读取。
  3. 所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
  4. "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
  5. 读取到一个异步任务,首先是将异步任务放进事件表格(Event table)中,当放进事件表格中的异步任务完成某种事情或者说达成某些条件(如setTimeout事件到了,鼠标点击了,数据文件获取到了)之后,才将这些异步任务推入事件队列(Event Queue)中,这时候的异步任务才是执行栈中空闲的时候才能读取到的异步任务。
2.2 Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
Event Loop是javascript的执行机制

宏任务和微任务

宏任务包括:setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务包括: Promises, Object.observe, MutationObserver

1.宏任务、微任务的执行顺序

先执行同步代码,遇到异步 宏任务 则将 异步 宏任务 放入宏任务队列中,遇到 异步 微任务 则将异步 微任务 放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
和我一起完全理解javascript中的宏任务与微任务_第2张图片

举例说明:


setTimeout(function(){
     
	console.log('1');
});
new Promise(function(resolve){
     		    
	console.log('2');
	resolve();
}).then(function(){
     		    
	console.log('3');
}); 		
console.log('4');
//正确输出 2,4,3,1

输出顺序解释:

  1. 遇到setTimout,异步宏任务,放入宏任务队列中; 遇到new Promise,new
  2. Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2;
  3. 而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
  4. 遇到同步任务console.log(‘4’);输出4;主线程中同步任务执行完
  5. 从微任务队列中取出任务到主线程中,输出3,微任务队列为空
  6. 从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空,结束~

下一个例子:

setTimeout(()=>{
     
  new Promise(resolve =>{
     
  	resolve();
  }).then(()=>{
     
  	console.log('test');
  });

  console.log(4);
});

new Promise(resolve => {
     
  resolve();
  console.log(1)
}).then( () => {
     
  console.log(3);
  Promise.resolve().then(() => {
     
    console.log('before timeout');
  }).then(() => {
     
    Promise.resolve().then(() => {
     
      console.log('also before timeout')
    })
  })
})
console.log(2);
//正确输出:1,2,3,before timeout ,also before timeout,4,test

输出顺序解释:

  1. 遇到setTimeout,异步宏任务,将() => {console.log(4)}放入宏任务队列中;
  2. 遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1;
  3. 而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
  4. 遇到同步任务console.log(2),输出2;主线程中同步任务执行完
  5. 从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中
  6. 从微任务队列中取出任务a到主线程中,输出 before timeout;
  7. 从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中;
  8. 从微任务队列中取出任务c到主线程中,输出 also before timeout;微任务队列为空
  9. 从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空
  10. 从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空,结束~

参考文章1:宏任务微任务
参考文章2:执行机制

你可能感兴趣的:(javascript,面试知识点)