js宏任务和微任务

事件循环就是Event Loop,是javascript一个特殊的地方.特殊就在于javascript是单线程语言,注定了异步操作有别于多线程语言.执行栈和任务队列是javascript执行异步任务的管理方式,

宏任务和微任务是js异步任务更细粒度的划分

单线程


进程和线程是操作系统中的概念,线程是进程的最小单位,一个进程可以包含多个线程,此处不再赘述。

javascript在设计之初就是单线程,程序运行时,只有一个线程存在,在特定的时候只能有特定的代码被执行.这和javascript用途有关;

它是一门浏览器脚本语言,通常是用来操作DOM的,如果是多线程,一个线程进行了删除DOM操作,另一个添加DOM,此时该如何处理;

所以javasecript在设计之初就是便是单线程的

 

虽然HTML5增加了 web Work 可用来另开一个线程,但是该线程仍受主线程的控制,没有Window对象,不能操作DOM;

所以javascript本质依然是单线程.所有同步、异步任务都是在主线程中执行

 

单线程的javascript一段一段的执行,前面的执行完了,再执行后面的;

试想一下,如果前一个任务需要执行很久,比如说接口请求、I/O操作,那后面的任务只能干等吗? : 干等不仅浪费了资源,而且页面交互程度也很差.

javascript意识到了这个问题,他们将任务分成了 同步任务和异步任务,对于二者有不同的处理

 

明确什么是同步任务、异步任务

 

1 let name = '小明'                   //同步任务1
2 window.addEventListener('popstate', (e) => { //同步任务2
3     console.log  (e)   //异步任务
4     })        

这里是两条语句,一是生命name并赋值,而是添加 window 的 popstate事件监听.

对应的这里产生了两个任务,是同步任务 , 主线程按照代码书写顺序先执行任务1(name声明和赋值),

在执行同步任务2: (添加window的popstate事件的监听).

监听器回调里要去执行console.log(e),所以这条语句也产生了一个任务,而它是一个异步任务,确切的说是整个回调函数产生了一个异步任务

 

//js代码: 同步代码  异步代码

//只写同步代码,js的执行顺序会非常清晰,自上而下执行

//异步代码: setTimeout  setInterval Promise process.nextTick

// 宏任务 和 微任务 关系到js代码的执行顺序

宏任务: script(全局作用域中的同步代码) setTimeout , setInterval

微任务: Promise, process.nextTick(node.js环境)

//宏任务队列

//微任务队列

1 //全局作用域: 立即添加到宏任务执行
2 console.log(123)
3 //使用setTimeout延迟执行函数,js执行到setTimeout时候,里面函数先注册,2秒后添加到宏任务中去
4 setTimeout(function(){
5  console.log('456')
6 },2000);
7 // js执行顺序,由外面的区域到里面的区域,宏任务和微任务交替执行

 

执行栈、任务队列


/**
详细分析 异步宏任务 异步微任务执行顺序
1.明确什么是同步任务, 什么是异步任务
2.执行栈, 任务队列
首先,执行栈管理同步任务,任务队列管理异步任务.同步任务没啥、异步任务分为 异步宏任务, 异步微任务
 常见宏任务 和微任务: 文章上文
*/
1
console.log(1) //同步任务A 2 3 4 setTimeout( //同步任务B 5 () => {console.log(2)} //任务B产生的异步宏任务 6 , 300) 7 8 new Promise( //同步任务C 9 (resolve) => { 10   console.log(3); 11 resolve(4); 12   }).then( //任务C执行过程中resolve(4)语句产生的异步微任务 13 (num) => {console.log(num)}) 14 15 setTimeout( //同步任务D 16 ()=> { console.log(5) } //任务D产生的异步宏任务 17 , 800)


/**
1.
javascript执行时,同步任务会排好队,在主线程上执行A, 完了B, B完了C ......知道执行栈里无其他任务,
排队的地方叫执行栈,执行A没啥问题,执行到B时,B是一个setTimeout任务,我们知道它会产生一个异步任务,但不是马上产生,而是等待300MS产生一个异步宏任务
这个异步任务的目的时去打印数字2,这是主线程没有傻傻等待300MS再去打印2,而是将任务挂起,继续往下走去执行同步任务C

2.主线程继续往下执行同步任务C,这时候同步任务C有俩部分,一是console.log(3),一是resolve(4),所以先后执行console.log(3),resolve(4),
我们知道resolve(4)执行会返回promise对象, 而promise对象里有自己的执行代码,(这段代码就是console.log(num)),
那么主线程是继续往下执行下一个同步任务? 还是转到执行resolve(4)返回的promise对象中的代码呢?
答案就是会执行下一个同步任务! 这个时候resolve(4)所返回的promise对象就产生了一个异步微任务,主线程将它挂起,挂起的方法就是将它塞到异步微任务执行队列当中,先不管他

3.下一个任务还是setTimeout,和上一个一样,只不过这个要等800ms才会产生一个异步宏任务,
回头看看之前产生的那个异步宏任务去哪了? 别急,300ms还没到呢,
主线程一路从 A 往下 D 这个期间异步任务BB 还没创建好呢,创建好后主线程会把它挂起来.
这个时候你可能有疑惑,我去,如果B那里设置延时不是300ms,而是3ms或者0呢?岂不是执行顺序会根据电脑性能乱掉?
这里如果将 setTimeout 的第二个参数设置为 0,它表示主线程空闲之后尽早执行它的回调,
HTML5 规定 setTimeout 的第二个参数不得小于 4 毫秒。
 
4.300ms时间到,异步宏任务BB 被创建成功,但也没有立即执行,而是被主线程塞到宏任务执行队列
 
5.执行栈空了,就把微任务执行队列中任务全部执行完
 
6.从宏任务队列中取出任务放到执行栈继续执行,此时宏任务 DD 也创建好了,被塞到宏任务执行队列,等待被取出放到执行栈执行
 
7. 执行完一个宏任务后,再去执行所有的微任务,
微任务执行完成以后,再取出最后一个宏任务 DD 放到执行栈执行.
后面每执行完一个宏任务,都去照顾一下微任务.直到所有的宏任务、微任务都执行完
 
总结: 
总结javascript处理同步异步任务的方式是: 用栈和队列调度分配任务,
代码首次运行主线程按照同步任务顺序,依次执行,执行期间产生的异步宏任务则挂起不执行,挂起的方式就是添加到宏任务执行队列;
产生的微任务也挂起,挂起的方式是添加到微任务执行队列;
待所有同步任务执行完成: 执行栈空了? :,就去把微任务队列中任务全部执行完
 
 
另外一篇总结: 主线程闲下来后先去把微队列中任务全部执行完,然后再去执行宏队列中的任务b,b执行完成后去微队列中check一遍发现没有任务了,此时返回宏队列也没有可执行的任务,至此整个代码也就执行完毕
代码:
 1 console.log(1); //任务A
 2 setTimeout(function(){ //任务B
 3   console.log(2) //任务B产生的异步宏任务b
 4 }, 200);  
 5 new Promise(     //任务C
 6     (resolve) => {   
 7         console.log(3) 
 8         resolve(4)
 9   }
10  )
11 .then(     //任务C执行过程中resolve(4)语句产生的异步微任务c
12      (num) => {   console.log(num)   }
13 )
14 console.log(5); //任务D

 

 
 
1.如果执行栈当前执行完的是宏任务,那么接下去就要执行微任务
2.如果执行栈当前执行完的是微任务,那么接下去会执行下一个微任务,如果不存在下一个微任务的话,那么接下去就要执行宏任务
 
其他要注意的:promise的回调体现在then和catch中,而promise中的代码是会当作同步任务执行的。
 
*/

 


posted @ 2020-05-31 17:22  看我几分像从前  阅读(223)  评论(0)    收藏  举报