js 单线程、宏任务与微任务的区别以及执行顺序是什么

150 篇文章 2 订阅
31 篇文章 0 订阅

目录

一、js 单线程

二、什么是宏任务?

1、定义

2、程序中哪些会被归为宏任务队列?

3、举例:

三、什么是微任务?

1、定义

2、程序中哪些会被归为微任务队列?

3、举例:

四、宏任务和微任务的区别

1、微任务会在宏任务之前执行。

2、我们在工作常用到的宏任务是 setTimeout,而微任务是 Promise.then

3、注意这里的Promise().then()

五、执行顺序 

六、程序题例子


一、js 单线程

众所周知,JavaScript是单线程的,也就是只有一个调用栈,调用栈按照先入后出的规则进行一次调用一个,而且可以嵌套。

在ES5之后,JavaScript引入了Promise,这样不需要浏览器,JavaScript引擎自身也能够发起异步任务了。所以,js是可以执行同步和异步任务的。

在执行调用栈的时候,会先执行同步任务(同步任务是不需要等待的任务)。调用栈在发现异步任务的时候,会把异步任务放入队列中,异步任务队列分为宏任务队列和微任务队列(队列都按照先入先出的规则)

同步的任务众人皆知是按照顺序去执行的;

而异步任务的执行,是有一个优先级的顺序的,包括了宏任务(macrotasks)微任务(microtasks)。

可以把宏任务理解成硬菜,微任务理解成软菜。一般是先上软菜,再上硬菜。

二、什么是宏任务?

1、定义

宏任务:指消息队列中的等待被主线程执行的事件

宏任务执行时都会重新创建栈,然后调用宏任务中的函数,栈也会随着变化。但宏任务执行结束时,栈也会随之销毁。宏任务是由宿主(浏览器/Node)发起的。

2、程序中哪些会被归为宏任务队列?

整体代码script,setTimeout,setInterval ,setImmediate,Promise*本身 ,I/O,

UI rendering , requestAnimationFrame 等~~

3、举例:

setTimeout(function(){
    console.log('1')
});

三、什么是微任务?

1、定义

微任务:可以看成是一个需要异步执行的函数

微任务是基于消息队列、事件循环、UI 主线程还有堆栈而来的。微任务是由JS引擎发起的。

微任务的执行时机:在主函数执行结束之后、当前宏任务结束之前

2、程序中哪些会被归为微任务队列?

Promises.(then catch finally)Async/Await  ,Object.observe , 

MutationObserver , process.nextTick(node)

3、举例:

new Promise(function(resolve){
    console.log('2'); // 主线程
    resolve();
}).then(function(){
    console.log('3') // 微任务
});

四、宏任务和微任务的区别

宏任务和微任务的区别在于在事件循环机制中,执行的机制不同

每次执行完所有的同步任务后,会在任务队列中取出异步任务,先将所有微任务执行完成后,才会执行宏任务

所以可以得出结论:

1、微任务会在宏任务之前执行。

主线程任务 => 异步任务(微任务 => 宏任务)

2、我们在工作常用到的宏任务是 setTimeout,而微任务是 Promise.then

3、注意这里的Promise().then()

new Promise在实例化的过程中所执行的代码是同步的,而在 then中注册的回调函数才是异步的!!!

new Promise(function(resolve){
    console.log('1'); // 主线程任务
    resolve();
}).then(function(){
    console.log('2') // 微任务
});

五、执行顺序 

如何运行就需要靠Event Loop事件循环:

在调用栈中先执行同步任务

期间,遇到宏任务就将其加入宏任务队列,遇到微任务就将其加入微任务队列。

② 在同步任务执行结束之后,调用栈会被清空,事件循环优先执行微任务

(也就是说,每次宏任务结束之后事件循环就会先执行微任务),直到微任务队列里面的任务被清空才会执行下一轮宏任务(并不会把宏任务全部执行了再执行微任务,这样子就可以避免因为宏任务繁重导致任务的阻塞)。

③ 微任务队列被清空的时候,不会立即执行宏任务,会优先看下浏览器是否需要渲染,渲染后再执行宏任务。

script此时也退出了宏任务,因为setTimeout其实是要等到script结束后才会执行回调,所以前面有个alert的时候,浏览器的渲染任务会被卡住,setTimeout也不会执行回调。

④ 如果此时宏任务的其中一轮结束后,也就是调用栈清空的时候,事件循环又发现了微任务。还会执行微任务,并且清空微任务队列,看下是否需要渲染,渲染完之后事件循环再执行下一轮宏任务,在没有其余微任务的时候,宏任务就可以一直清空宏任务队列,这就是整个事件循环流程。

 

六、程序题例子

setTimeout(function(){
    console.log('1')
});

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

console.log('4');

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

setTimeout(function(){
    console.log('7')
});

function bar(){
    console.log('8')
    foo()
}

function foo(){
    console.log('9')
}

console.log('10')
bar()

解析: 

首先浏览器执行Js代码由上至下顺序,遇到setTimeout,把setTimeout分发到宏任务Event Queue中


new Promise属于主线程任务,直接执行 => 打印2


Promis下的then方法属于微任务,把then分到微任务 Event Queue中


console.log(‘4’)属于主线程任务,直接执行 => 打印4


又遇到new Promise,直接执行 => 打印5

Promise 下到then分发到微任务Event Queue中


又遇到setTimouse也是直接分发到宏任务Event Queue中,等待执行


console.log(‘10’)属于主线程任务,直接执行 =>  打印10


主线程内容:

遇到 bar() 函数调用,执行构造函数内的代码  =>   打印8

在bar函数中调用foo函数,执行foo函数内的代码  =>  打印9


主线程中任务执行完后,

就要执行分发到微任务Event Queue中代码,实行先进先出 => 依次打印3,6


微任务Event Queue中代码执行完,

就执行宏任务Event Queue中代码,也是先进先出 => 依次打印1,7。


最终结果:2,4,5,10,8,9,3,6,1,7

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值