JS执行机制(同步、异步[宏任务、微任务])

众所周知,JavaScript是一门单线程语言,等到上一个任务结束之后才会进行下一个任务。但是如果上一个任务迟迟没有结束,那下一个任务就会卡在原地无法动弹。

这时,我们就将所有的任务分为两类:同步任务和异步任务

什么是同步和异步?

  • 同步:后一个任务等待前一个任务结束后才会开始执行。
  • 异步:后一个任务无需等待前一个任务结束就可以执行。

为什么会出现异步任务?

  • JS是单线程语言,同一时间只能做一件事,为了提高性能,在执行过程中不阻塞后续代码的执行。通常会采用异步操作。

如何执行同步异步?

(1)同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。

(2)所有同步任务都在主线程上排队依次执行,形成一个执行栈。

(3)主线程之外,还存在一个"任务队列"(task queue)。异步任务进入Event Table并注册函数,异步任务有了运行结果,就在“任务队列”之中放置一个事件。

(4)一旦主线程中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入主线程执行。

(5)主线程内的任务执行完毕为空,就会从"任务队列"中读取事件,这个过程是循环不断的,形成event loop(事件循环)。

以上也就是JS的初步执行机制(事件循环)。

那怎么才能判断出主线程的执行栈是否为空呢?

答:js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

什么是宏任务和微任务?

一般情况,我们将异步任务又分为宏任务和微任务。

  • 宏任务:包含整个script代码块,setTimeout, setIntval、setImmediate、Ajax、DOM事件。
  • 微任务:Promise.then catch finally , process.nextTick。

以上内容纯八股文,记住就好。

虽说宏任务与微任务都是异步任务,那它们的执行顺序也是一样的吗?

显而易见,答案是不一样。

事实是,微任务的执行时间比宏任务要早!

所以我们在执行完同步任务之后开始执行异步任务的顺序应该是:

  • 每个宏任务队列里面有微任务队列,执行宏任务的过程中遇到微任务,就把微任务加入到微任务队列中;
  • 当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行;
  • 微任务执行完毕后再将异步宏任务从队列中调入主线程执行。
  • 一直循环直至所有任务执行完毕。

通俗地讲,则是以下顺序:

执行同步代码 ==> 检查微任务并执行 ==> 执行宏任务1 ==> 检查微任务并执行 ==> 执行宏任务2 ==> 检查微任务并执行 ==> 执行宏任务3 …

这算是更精确的JS执行机制。

找个很经典的例题看一下吧。

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. 这段代码作为宏任务,进入主线程。
  2. 先遇到setTimeout,那么将其回调函数注册后分发到宏任务event queue.
  3. 接下来遇到Promise, new Promise立即执行,then函数分发到微任务event queue
  4. 遇到console.log(), 立即执行
  5. 整体代码script作为第一个宏任务执行结束, 查看当前有没有可执行的微任务,执行then的回调。(第一轮事件循环结束了,我们开始第二轮循环)
  6. 从宏任务的event queue开始,我们发现了宏任务event queue中setTimeout对应的回调函数,立即执行。

猜你喜欢

转载自blog.csdn.net/weixin_46953330/article/details/129562667