首页 > 其他分享 >JS事件循环(event loop)

JS事件循环(event loop)

时间:2022-09-05 09:35:33浏览次数:65  
标签:执行 console log 队列 JS 任务 setTimeout event loop

事件循环概述

事件循环是用来实现异步特性的。

事件循环中的几个概念:

主线程:

  • 理解为同步任务的先进先出,一旦调用,同步任务就执行。

执行栈:

  • 先入后出的数据结构,一个任务来到栈底就立即执行,然后释放。

任务队列:

  • 包括宏任务队列和微任务队列,当执行栈空的时候,就会从任务队列中,取任务来执行。

宏任务和微任务:

  • 异步任务,通常在主线程执行完同步任务完成之后,才会来执行宏任务和微任务。微任务的优先级要比宏任务高。

常见的宏任务:

  • script(全局任务)、setTimeout、setInterval、setImmediate、I/O、UI Rendering。

常见的微任务:

  • Process.nextTick、Promise、Object.observer、MutationObserver、queueMicrotask(将函数添加到微任务队)

注意:new Promise的过程属于同步任务,resove或者reject之后才算微任务。


执行过程

image

主线程首先执行完同步任务,然后会去任务队列中执行宏任务,如果在执行宏任务的过程中发现有微任务,这时候微任务比宏任务先执行。全部执行完成之后等待主线程调用,调用完成之后再去任务队列中查看是否还有异步任务有待执行,循环往复。


事件循环详解

在实际开发中,往往是同步代码和异步代码都有。在js执行时,还是从第一行代码开始执行,遇到函数就将其添加到栈中,然后执行同步操作;如果遇到异步函数,则根据其类型,宏任务就添加到宏任务队列,微任务添加到微任务队列。直到同步代码执行完毕,则开始执行异步操作。

异步操作后于同步操作,异步操作内部也是分先后顺序的。总的来说:

  • 微任务先于宏任务执行
  • 微任务与微任务之间根据先后顺序执行,宏任务与宏任务之间根据延迟时间顺序执行
  • 微任务在下一轮DOM渲染前执行,宏任务在下一轮DOM渲染之后执行
  • 每个任务的执行都是一次出栈操作,直到栈被清空

微任务在下一轮DOM渲染前执行,宏任务在之后执行

let div = document.createElement('div');
div.innerHTML = 'hello world';
document.body.appendChild(div);
let list = document.getElementsByTagName('div');
console.log('同步任务 length ---', list.length);
console.log('start');
setTimeout(() => {
  console.log('setTimeout length ---', list.length);
  alert('宏任务 setTimeout 阻塞'); // 使用alert阻塞js执行
}, 1000);
Promise.resolve().then(() => {
  console.log('promise then length ---', list.length);
  alert('微任务 promise then 阻塞');
});
console.log('end');

// 运行结果:
// 同步任务 length --- 1
// start
// end
// promise then length --- 1
// setTimeout length --- 1
// alert('微任务 promise then 阻塞'),此时的页面是白屏,因此处于DOM渲染前
// alert('宏任务 setTimeout 阻塞'),此时页面出现了hello world,因此处于DOM渲染后

微任务中创建宏任务

new Promise((resolve) => {
  console.log('promise 1');
  setTimeout(() => {
    console.log('setTimeout 1');
  }, 500);
  resolve();
}).then(() => {
  console.log('promise then');
  setTimeout(() => {
    console.log('setTimeout 2');
  }, 0);
});
new Promise((resolve) => {
  console.log('promise 2');
  resolve();
});

// 运行结果:
// promise 1
// promise 2
// promise then
// setTimeout2
// setTimeout 1

解析:

js执行代码,遇到两个Promise,则分别添加到微任务队列,同步代码执行完毕。

在微任务队列中根据先进先出,第一个Promise先执行,遇到setTimeout,则添加到宏任务队列,resolve()返回执行结果并执行then,事件循环将其继续添加到微任务队列;第一个Promise执行完毕,执行第二个Promise。

继续执行微任务队列,直到清空队列。遇到setTimeout,并将其添加到宏任务队列。

宏任务队列现在有两个任务待执行,由于第二个setTimeout的延迟事件更小,则优先执行第二个;如果相等,则按照顺序执行。

继续执行宏任务队列,直到清空队列。


宏任务中创建微任务

setTimeout(() => {
  console.log('setTimeout 1');
  new Promise((resolve) => {
    console.log('promise 1');
    resolve();
  }).then(() => {
    console.log('promise then');
  })
}, 500);
setTimeout(() => {
  console.log('setTimeout 2');
  new Promise((resolve) => {
    console.log('promise 2');
    resolve();
  })
}, 0);

// 运行结果:
// setTimeout2
// promise2
// setTimeout1
// promise1
// promise then

解析:

js执行代码,遇到两个setTimeout,将其添加到宏任务队列,同步代码执行完毕。

先检查微任务队列中是否有待处理的,刚开始肯定没有,因此直接执行宏任务队列中的任务。第二个为零延迟,需要优先执行。遇到Promise,将其添加到微任务队列。第一个宏任务执行完毕。

在执行第二个宏任务时,微任务队列中已经存在待处理的,因此需要先执行微任务。

微任务执行完毕,并且延迟时间到期,第一个setTimeout开始执行。遇到Promise,将其添加到微任务队列中。

执行微任务队列中的Promise,执行完毕后遇到then,则将其继续添加到微任务队列。

直到所有微任务执行完毕。


宏任务中创建宏任务

setTimeout(() => {
  console.log('setTimeout 1');
  setTimeout(() => {
    console.log('setTimeout 2');
  }, 500);
  setTimeout(() => {
    console.log('setTimeout 3');
  }, 500);
  setTimeout(() => {
    console.log('setTimeout 4');
  }, 100);
}, 0);

// 运行结果
// setTimeout1
// setTimeout4
// setTimeout2
// setTimeout3

无需解析。


微任务中创建微任务

new Promise((resolve) => {
console.log('promise 1');
new Promise((resolve) => {
  console.log('promise 2');
  resolve();
});
new Promise((resolve) => {
  console.log('promise 3');
  resolve();
})
  resolve();
})

// 运行结果:
// promise1
// promise2
// promise3

无需解析。

标签:执行,console,log,队列,JS,任务,setTimeout,event,loop
From: https://www.cnblogs.com/luckest/p/16656933.html

相关文章

  • json基础
      json内可以包括任何字符类型varJsonObj={"key":value,"key2":"abc","key3":true......
  • 使用 Node.js 构建基于 Typescript 的命令行界面 (CLI)
    使用Node.js构建基于Typescript的命令行界面(CLI)我们的目标是使用Typescript创建一个基于Node.js的命令行界面(CLI)。此设置包括顶级等待支持和ES模块导入。......
  • JS | getBoundingClientRect()方法的基本使用
    Element.getBoundingClientRect()方法返回一个DOMRect对象,其提供了元素的大小及其相对于视口的位置。返回的对象中有几个只读属性:width/x:元素矩形的宽度height/y......
  • 【JS每日一题】Array.reduce函数
    题目题目来源于前端面试题宝典[[0,1],[2,3]].reduce((acc,cur)=>{returnacc.concat(cur)},[1,2])解析[1,2]会作为初始值首次放入到第一个参数......
  • 【JS】112. 路径总和
    112.路径总和代码DFSvarhasPathSum=function(root,targetSum){//找到没有根了,那么就说明这条路行不通if(!root){returnfalse;}//......
  • 2022-08-27 田龙跃 web前端(JS)
    原生JS数据类型Number-数字String-字符串Boolean-布尔型null-空undefined-未定义变量(同var功能相同)letnum1=“das”(let会自己检查变量是否重复定义)constnum......
  • CSS JS 规范+数据类型
    1、CSSJS规范+数据类型window.onload=function(){​//varstr='abc';​//varnum=123;​//varbool=true;​//varund=undefined;......
  • 1.JS快速入门
    1.引入JavaScript1.1引入JavaScript1.内部标签 <script>   alert('HelloWorld!'); </script>2.外部引入xxx.js ....test.html <scriptsrc="xxx.js"......
  • js 实现计数排序
    //计数排序//稳定性:稳定//定义一个数组,将数组中每个元素出现的次数以数组形式保存起来,数组索引值即为具体key,数组索引对应的元素值即为该索引值出现的次数//再将......
  • JS中校验身份证号
    //1城市代码列表varaIdentityCode_City={11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江",31:......