JavaScript事件循环、同步、异步、宏任务、微任务(超详细)

javascript从诞生之日起就是一门单线程(setTimeout()的回调函数是在主线程执行的)的非阻塞(事件循环)的脚本语言。在同一个时间只能做一件事情,这就导致后面的任务需要等到前面的任务完成才能执行,如果前面的任务很耗时就会造成后面的任务一直等待。

  • 同步任务:在主线程上排队执行的任务只有前一个任务执行完毕,才能执行后一个任务。
  • 异步任务:不进入主线程,而是进入任务队列。

当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行。由于主线程不断重复的获得任务、执行任务、再获取再执行,所以者种机制被叫做事件循环(Event Loop)

异步任务主要分为宏任务微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起的。事件循环的过程中,执行栈在同步代码执行完成后,优先检查微任务队列是否有任务需要执行,如果没有,再去宏任务队列检查是否有任务执行,如此往复。微任务一般在当前循环就会优先执行,而宏任务会等到下一次循环,因此,微任务一般比宏任务先执行,并且微任务队列只有一个,宏任务队列可能有多个。

  • 宏任务:setTimeout、setInterval、Ajax、DOM事件、script整体代码块、I/OUI交互事件、postMessageMessage、Channel setImmediate(Node.js 环境)
  • 微任务:Promise.[ then/catch/finally ]、async/await、Object.observe、MutationObserver(浏览器环境)、process.nextTick(Node环境)注意new Promise是同步执行的,和它的then()等方法区分。

举例1:

console.log('同步代码1');       1,执行第一个同步任务

setTimeout(() => {
    console.log('setTimeout')   5,执行宏任务
}, 0)

new Promise((resolve) => {
  console.log('同步代码2')      2,执行第二个同步任务
  resolve()
}).then(() => {
    console.log('promise.then') 4,执行微任务
})

console.log('同步代码3');       3,执行第三个同步任务
// 最终输出"同步代码1"、"同步代码2"、"同步代码3"、"promise.then"、"setTimeout"

顺便提一下,在浏览器中 setTimeout 的延时设置为 0 的话,会默认为 4ms,NodeJS 为 1ms。具体值可能不固定,但不是为 0。

注意:new Promise 是同步执行的,promise.then 里面的回调才是异步的。

举例2:

setTimeout(() => {
  console.log('setTimeout start');    5,执行宏任务里的第一个任务
  new Promise((resolve) => { 
    console.log('promise1 start');     6,执行宏任务里的第二个任务
    resolve();
  }).then(() => {
    console.log('promise1 end');      8,执行宏任务里的微任务
  })
  console.log('setTimeout end');      7,执行宏任务里的第三个任务
}, 0);
function promise2() {
  return new Promise((resolve) => {
    console.log('promise2');         3,执行第一个微任务
    resolve();
  })
}
async function async1() {
  console.log('async1 start');       1,执行第一个同步任务
  await promise2();
  console.log('async1 end');          4,执行第二个微任务
}
async1();
console.log('script end');           2,执行第二个同步任务

async/await 微任务可以理解为:当执行第一个同步任务时(调用async1函数),输出async1 start;当执行微任务时,将await关键字后面的全部视为微任务,依次输出为 promise2、async1 end。

async1 start
script end
promise2
async1 end
setTimeout start
promise1 start
setTimeout end
promise1 end

举例3:

// 宏任务队列 1
setTimeout(() => {
  // 宏任务队列 2.1
  console.log('timer_1');
  setTimeout(() => {
    // 宏任务队列 3
    console.log('timer_3')
  }, 0)
  new Promise(resolve => {
    resolve()
    console.log('new promise')
  }).then(() => {
    // 微任务队列 1
    console.log('promise then')
  })
}, 0)
 
setTimeout(() => {
  // 宏任务队列 2.2
  console.log('timer_2')
}, 0)
console.log('========== Sync queue ==========')

父宏任务中嵌套子宏任务,先执行完第一个父宏任务外层的所有宏任务,最后执行嵌套的子微任务。所以先是time_2,后是time_3

1 timer_1
2 new promise
3 promise then
4 timer_2
5 timer_3

举例4:

 async function async1() {
       console.log('async1 start');   2,第二个同步任务
       await async2();                
       console.log('async1 end');     5,第二个微任务
   }
   async function async2() {
       console.log('async2');          4,第一个微任务
   }
   
   console.log('script start');       1,第一个同步任务
   
   setTimeout(function() {
       console.log('setTimeout');     7,执行宏任务
   }, 0)
   
   async1();
   
   new Promise(function(resolve) {
       console.log('promise1');        3,第三个同步任务
       resolve();
   }).then(function() { 
       console.log('promise2');        6,第三个微任务
   });
   console.log('script end');           4,第四个同步任务
script start
async1 start
promise1
script end
async2
async1 end
promise2
setTimeout

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rinba_murphy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值