JS高级——浏览器事件循环-微任务、宏任务

 JS代码在浏览器中的执行过程在 JS高级——浏览器运行前端项目的原理及流程里面已经介绍了,但是文章中执行的代码都只是一般情况的代码执行,都是从上往下依次执行;实际上在开发中我们会经常使用网络请求(axios)、promiese、setTimeOut、setInterval等异步操作时,那么在执行代码时浏览器会按照什么样的执行顺序来执行呢?接下来让我们来了解浏览器的事件循环机制。

1、浏览器中的JavaScript线程

        操作系统中的进程和线程,在这里就不过多解释了,不了解的小伙伴可以查询一下资料。

  1.1 我们知道JavaScript是单线程的,它的容器进程是浏览器或Node。那么浏览器是单个进程吗?进程里面只有一个线程吗?

        答案是目前多数的浏览器其实都是多进程的,当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出;每个进程中又有很多的线程,其中包括执行JavaScript代码的线程。

  1.2 JavaScript的代码执行是在一个单独的线程中执行的

        这就意味着JavaScript的代码,在同一个时刻只能做一件事;如果这件事是非常耗时的,就意味着当前的线程就会被阻塞,所以真正耗时的操作,实际上并不是由JavaScript线程在执行的,是由浏览器的其他线程来完成的,比如网络请求、定时器,我们只需要在特定的时候执行应该有的回调即可。

2、浏览器的事件循环

  2.1 首先我们先来看下事件循环的流程图

        (1)JavaScript线程执行JS代码,会将异步操作分发给浏览器其他线程进行操作;

        (2)然后对异步操作进行分类,划分为微任务队列和宏任务队列;

        (3)最后调用栈会对循环队列中的函数进行回调,在调用栈中执行;

        那么现在问题来了,我们怎么知道异步操作是属于宏任务还是属于微任务?调用栈在调用循环队列中的函数时,调用的优先级是怎么样的呢?

  2.2 宏任务和微任务

        (1)宏任务队列:ajax、setTimeout、setInterval、DOM监听、UI Rendering等;

        (2)微任务队列:Promise的then回调、 Mutation Observer API、queueMicrotask()等;

  2.3 宏任务和微任务优先级

        (1)优先级最高:编写的顶层JS代码,如图中除去setTimeOut函数中的其他代码;

        (2)微任务优先级大于宏任务:在执行每个宏任务之前,要先查看微任务队列中是否有微任务需要执行,如果有则先执行微任务;如果没有则执行当前宏任务。

  2.4 事件循环测试题

        (1)测试题一

setTimeout(function () {
  console.log("setTimeout1");
  new Promise(function (resolve) {
    resolve();
  }).then(function () {
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2");
  });
});

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("then1");
});

setTimeout(function () {
  console.log("setTimeout2");
});

console.log(2);

queueMicrotask(() => {
  console.log("queueMicrotask1")
});

new Promise(function (resolve) {
  resolve();
}).then(function () {
  console.log("then3");
});

        我们来画图解析,首先画出三个框分别表示输出值、微任务列表、宏任务列表,顺序都是从上到下,开始都是空的;代码部分内容较多,圈出来使用标签代替;执行玩的部分划掉。如图:

(1)执行第1行,是一个setTimeOut,属于宏任务,所以将part1部分放入宏任务队列;

(2)执行第15行,是一个Promise,函数参数直接执行,所以main script中写入promise1;promise.then()属于微任务,所以将then1放入微任务队列;

(3)执行22行,setTimeOut属于宏任务,将setTimeOut2部分放入宏任务队列;

(4)执行26行,直接输出,main script放入2;

(5)执行28行,queueMicrotask属于微任务,将queueMicrotask1放入微任务队列;

(6)执行32行,promise.then 属于微任务,将then3放入微任务;

        此时,直接执行代码已执行完,下面执行微任务队列和宏任务队列,微任务优先级大于宏任务。

(7)执行微任务then1,将then1放入main script;

(8)执行微任务queueMicrotask1,将queueMicrotask1放入main script;

(9)执行微任务then3,将then3放入main script;

        此时,微任务队列为空,开始执行宏任务。

(10)执行宏任务part1,将setTimeOut1放入main script;Promise.then属于微任务,将part2放入微任务队列;

        此时,微任务中有part2,宏任务中有setTimeOut2,由于微任务优先级大,则执行微任务。

(11)执行微任务part2,Promise.then属于微任务,将part3放入微任务列表;将then2放入main script;

        此时,微任务中有part3,宏任务中有setTimeOut2,由于微任务优先级大,则执行微任务。

(12)执行微任务part2,将then4放入main script;

        此时,微任务队列为空,开始执行宏任务。

(13)执行setTimeOut2,将setTimeOut2放入main script;

        至此,所有代码执行完毕,输出结果顺序为main script中的内容。

        (2)测试题二  过程就不画了,可以自己动手画一画

async function async1() {
  console.log('async1 start')
  await async2();//其后面执行的代码相当于放进then中,作为微任务
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

async1();

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
}).then(function () {
  console.log('promise2')
})

console.log('script end')

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

   

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值