JavaScript-宏任务和微任务的理解

2,545 阅读4分钟

1.宏任务与微任务:

🍊🍊浏览器中的事件轮询 eventLoop,分为同步执行栈异步消息队列,首先会执行同步的任务,当同步任务执行完之后会从异步消息队列中取异步任务拿到同步执行栈中进行执行

宏任务与微任务

  • 宏任务:优先级低,先定义的先执行。包括:ajax,setTimeout,setInterval,事件绑定,postMessage,MessageChannel(用于消息通讯)
  • 微任务,优先级高,并且可以插队,不是先定义先执行。包括:Promise 中的 then,observer,MutationObserver,setImmediate

🌰Section1:

setTimeout(function() {
  console.log(4);
}, 0);

new Promise(function(reslove) {
  console.log(1);
  reslove();
}).then(function(data) {
  console.log(3);
});

console.log(2);

//会输出:1,2,3,4。

🌵promise 中是同步任务,promise 的 .then 中是异步任务~!!!

上面的执行顺序就是先将 setTimeout 加入到异步消息队列的宏任务池中,然后执行 promise 中的console.log(1),再将 promise.then加到异步消息队列中微任务池中,再执行同步任务console.log(2),当同步任务都执行完之后,去微任务中的任务,执行console.log(3),微任务执行完之后取宏任务,执行console.log(4)。所以输出顺序就是:1,2,3,4。

🌰Section2拓展:

setTimeout(function() {
  console.log(4);
}, 0);

new Promise(function(reslove) {
  console.log(1);
  setTimeout(function() {
    reslove("我勒个擦MMP");    //这里需要注意,这里如果是reslove就不会执行的,如果是console.log就会执行
   console.log("你的名字叫十二");
  }, 0);
  reslove('first');
}).then(function(data) {
  console.log(data);
});

console.log(2);
//输出结果:
//1,2,first,4,你的名字叫十二

🌵具体过程:

这个时候就会输出:1,2,first,4,你的名字叫十二 没有输"我勒个擦MMP",有些人可能会想,应该输出 1,2,first,4,你的名字叫十二,我勒个擦MMP,这个时候就要明白一个事情,当使用 reslove 之后,promise 的状态就从 pedding变成了 resolve🍊promise 的状态更改之后就不能再更改了,所以 reslove('我勒个擦MMP') 就不会执行了。

当我们把 reslove('我勒个擦MMP') 改成 console.log('我勒个擦MMP 了') 的时候,他就会输出 1,2,first,4,你的名字叫十二,我勒个擦MMP 了。

🌰Section3究极进化:

setTimeout(function() {
  console.log(1);
}, 0);

new Promise(function(reslove) {
  console.log(2);
  reslove('p1');
  new Promise(function(reslove) {
    console.log(3);
    setTimeout(function() {
      reslove('setTimeout2');
      console.log(4);
    }, 0);
    reslove('p2');
  }).then(function(data) {
    console.log(data);
  });
  setTimeout(function() {
    reslove('setTimeout1');
    console.log(5);
  }, 0);
}).then(function(data) {
  console.log(data);
});

console.log(6);

//打印结果:
// 2,3,6,p2,p1,1,4,5

🌵具体执行过程:

先执行同步的任务,new Promise 中的都是同步任务,所以先输出 2,3,6,然后再执行微任务的,微任务可以插队,所以并不是先定义的 p1 先执行,而且先将 p2 执行,然后执行 p1,当微任务都执行完成之后,执行宏任务,宏任务依次输出1,4,5,promise 的状态不可以变更,所以 setTimeout1setTimeout2 不会输出。

🌵特别声明

node环境下,process.nextTick的优先级高于Promise,也就是说:在宏任务结束后会先执行微任务队列中的nextTickQueue,然后才会执行微任务中的Promise

总结:

  • JavaScript 是单线程语言,决定于它的设计最初是用来处理浏览器网页的交互。🍊浏览器负责解释和执行 JavaScript 的线程只有一个(所有说是单线程),即JS引擎线程,但是浏览器同样提供其他线程,如:事件触发线程、定时器触发线程等。
  • 异步一般指:
    • 网络请求
    • 计时器
    • DOM事件监听(这个注意下!)
  • 事件循环机制:
    • JS引擎线程只会维护一个执行栈,同步的代码会依次加入到执行栈中依次执行,然后出栈。
    • JS引擎线程遇到异步函数,会将异步函数交给相应的Webapi,而继续执行后面的任务。
    • Webapi会在条件满足的时候,将异步对应的回调加入到消息队列中,等待执行。
    • 执行栈为空的时候,JS引擎线程会去取消息队列中的回调函数(如果有的话),并加入到执行栈中执行。
    • 完成后出栈,执行栈再次为空,重复上面的操作,这就是事件轮询机制。