首页 > 其他分享 >EventLoop(事件循环)

EventLoop(事件循环)

时间:2023-10-25 17:22:49浏览次数:28  
标签:异步 console log 队列 EventLoop 任务 循环 事件 setTimeout

EventLoop(事件循环)

一、前言

JS任务分为同步任务(非耗时任务)和异步任务(耗时任务),异步任务又分为宏任务和微任务。

img

event loop:JS 主线程不断的循环往复的从任务队列中读取任务,执⾏任务,这种运⾏机制称为事件循环(event loop)

二、同步和异步

​ JS是单线程执行的语言,在同一个时间只能做一件事情。这就导致后面的任务需要等到前面的任务完成才能执行,如果前面的任务很耗时就会造成后面的任务一直等待。为了解决这个问题JS中出现了同步任务和异步任务。

同步任务:

​ 在主线程上排队执行的任务只有前一个任务执行完毕,才能执行后一个任务,形成一个执行栈。

异步任务:

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

​ 我们都知道 Js 是单线程的,但是一些高耗时操作带来了进程阻塞的问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)异步模式(Asynchronous)

​ 在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。

三、宏任务和微任务?

注意

​ 宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。

宏任务(Macrotask)包括:

1. 整体代码script;
2. 定时器任务: 如setTimeout、setInterval、setImmediate (NodeJS独有);
3. I/O操作:  网络请求,文件读写;
4. 渲染任务:dom渲染,当浏览器需要重绘或重新布局时触发的任务;
5. 异步ajax等;
6. 用户交互任务:例如点击事件、输入事件等与用户交互的相关任务;
7. 请求动画帧任务:通过requestAnimationFrame()方法设置的任务,用于在每一帧进行绘画或动画操作;

这些任务都是比较耗时的操作,在事件循环中被视为宏任务,需要等待一定时间或特定的触发条件才会执行

宏任务的执行顺序:setImmediate --> setTimeout --> setInterval --> i/o操作 --> 异步ajax

微任务(Microtask)包括:

1. Promise回调:Promise对象的resolve或reject方法的回调函数;Promise的then、catch、finally回调;
2. MutationObserver回调:当DOM发生变化时触发的回调函数;
3. async/await函数中的后续操作:在async函数中使用await等待的操作完成后,紧接着的代码块中的任务;
4. process.nextTick:进程对象process中的一个方法。nextTick会在上一次事件循环结束,然后在下一次事件循环开始之前执行。比setTimeout(fn,0)效率高多了。

微任务的执行顺序:process.nextTick --> Promise

四、执行顺序

js代码在执行的时候,会先执行同步代码 ==> 遇到异步宏任务则将异步宏任务放入宏任务队列中 ==> 遇到异步微任务则将异步微任务放入微任务队列中 ==> 当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行 ==> 微任务执行完毕后,再将异步宏任务从队列中调入主线程执行 ==> 一直循环至所有的任务执行完毕(完成一次事件循环EventLoop)。

注:一次事件循环只能处理一个宏任务,一次事件循环可以将所有的微任务处理完毕。

img

五、题目练习

练习一、

setTimeout(function () {//宏任务放到队列中
 console.log('1');
})
new Promise(function (resolve) {
 console.log('2'); //实例化过程是同步任务,直接执行
 resolve();
}).then(function () { //放到微任务队列中
 console.log('3');
})
console.log('4'); //同步任务,直接执行
//打印顺序 2 4 3 1

分析:

1. 遇到setTimeout,异步宏任务将其放到宏任务列表中,命名为time1;
2. new Promise 在实例化过程中所执行的代码都是同步执行的( function 中的代码),输出2 ;
3.  将 Promise 中注册的回调函数放到微任务队列中,命名为 then1 ;
4.  执行同步任务 console.log('4') ,输出 4 ,至此执行栈中的代码执⾏完毕;
5. 从微任务队列取出任务 then1 到主线程中,输出 3 ,至此微任务队列为空;
6. 从宏任务队列中取出任务 time1 到主线程中,输出 1 ,至此宏任务队列为空;

练习二、

console.log(1); //同步任务
setTimeout(function () { //宏任务
  console.log(2); //宏任务中的同步任务
  let promise = new Promise(function (resolve, reject) {
    console.log(3); //宏任务中的同步任务
    resolve();
  }).then(function () {
    console.log(4); //宏任务中的微任务
  });
}, 1000);
setTimeout(function () { //宏任务
  console.log(5); //宏任务中的同步任务
  let promise = new Promise(function (resolve, reject) {
    console.log(6); //宏任务中的同步任务
    resolve();
  }).then(function () {
    console.log(7); //宏任务中的微任务
  });
}, 0);
let promise = new Promise(function (resolve, reject) {
  console.log(8); //同步任务
  resolve()
}).then(function () {
  console.log(9); //微任务
}).then(function () {
  console.log(10); //微任务
});
console.log(11); //同步任务
//执行顺序:1 8 11 9 10 5 6 7 2 3 4

分析:

1. 执⾏同步任务 console.log(1) ,输出 `1` ;
2. 遇到 setTimeout 放到宏任务队列中,命名 time1 ;
3. 遇到 setTimeout 放到宏任务队列中,命名 time2 ;
4. new Promise 在实例化过程中所执⾏的代码都是同步执⾏的( function 中的代码),输出`8` ;
5. 将 Promise 中注册的回调函数放到微任务队列中,命名为 then1 ;
6. 将 Promise 中注册的回调函数放到微任务队列中,命名为 then2 ;
7. 执⾏同步任务 console.log(11), 输出 `11` ;
8. 从微任务队列取出任务 then1 到主线程中,输出` 9` ;
9. 从微任务队列取出任务 then2 到主线程中,输出 `10` ,⾄此微任务队列为空;
10. 从宏任务队列中取出 time2( 注意这⾥不是 time1 的原因是 time2 的执⾏时间为 0);
11. 执⾏同步任务 console.log(5) ,输出 `5` ;
12. new Promise 在实例化过程中所执⾏的代码都是同步执⾏的( function 中的代码),输出`6` ;
13. 将 Promise 中注册的回调函数放到微任务队列中,命名为 then3 ,⾄此宏任务time2执⾏完成;
14. 从微任务队列取出任务 then3 到主线程中,输出 `7` ,⾄此微任务队列为空;
15. 从宏任务队列中取出 time1 ,⾄此宏任务队列为空;
16. 执⾏同步任务 console.log(2) ,输出` 2` ;
17. new Promise 在实例化过程中所执⾏的代码都是同步执⾏的( function 中的代码),输出`3` ;
18. 将 Promise 中注册的回调函数放到微任务队列中,命名为 then4 ,⾄此宏任务time1执⾏完成;
19. 从微任务队列取出任务 then4 到主线程中,输出` 4 `,⾄此微任务队列为空。

练习三、

//宏任务执行顺序: setImmediate --> setTimeout --> setInterval --> i/o操作 --> 异步ajax
let axios = require('axios');
let fs = require('fs');
console.log('begin'); //同步任务
fs.readFile('1.txt',(err,data)=>{ //宏任务-读写
	console.log('fs');
});
axios.get('https://api.muxiaoguo.cn/api/xiaohua?api_key=fd3270a0a9833e20').then(res=>{ 
	console.log('axios'); //宏任务-异步的Ajax
});
setTimeout(()=>{ //宏任务-setTimeout
	console.log('setTimeout')
},0);
setImmediate(()=>{ //宏任务-setImmediate
  console.log('setImmediate');
});
(async function (){
	console.log('async') //微任务?
})();
console.log('end'); //同步任务
//执行顺序:begin async end setTimeout setImmediate fs axios

分析:

setImmediate没有时间参数,它与延迟 0 毫秒的 setTimeout() 回调⾮常相似。所以当setTimeout延迟时间也是0毫秒时,谁在前面就先执行谁。此外如果setTimeout延迟时间不是0毫秒,它的执行顺序会在 i/o 操作之后。

练习四、

//微任务之间的执行顺序:process.nextTick --> Promise
console.log('begin');
Promise.resolve().then(()=>{
  console.log('promise');
})
process.nextTick(()=>{
	console.log('nextTick');
});
console.log('end');
//执行顺序:begin end nextTick promise

练习五、

 // 宏任务队列 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 =====')
//执行顺序:===== Sync queue =====;timer_1 new;promise;promise then;timer_2;timer_3

练习六*、

async function async1() { //async--声明一个函数是异步的
   console.log('async1 start');
   await async2(); //await--等待一个异步函数执行完成
   console.log('async1 end'); //异步微任务---await等待的操作完成后,紧接着的代码块中的任务
}
async function async2() {
   console.log('async2');
}

console.log('script start'); //同步任务

setTimeout(function() {
   console.log('setTimeout'); //异步宏任务
}, 0)

new Promise(function(resolve) {
   console.log('promise1'); //同步任务
   resolve();
}).then(function() {
   console.log('promise2'); //异步微任务
});

async1();

console.log('script end'); //同步任务
//执行顺序:script start;promise1;async1 start;async2;script end;promise2;async1 end;setTimeout;

分析:

1. 声明一个异步函数async1;
2. 声明一个异步函数async2;
3. 执行同步任务console.log('script start'),输出`script start`;
4. 遇到 setTimeout 放到宏任务队列中,命名 time1 ;
5. new Promise 在实例化过程中所执⾏的代码都是同步执⾏的( function 中的代码),输出`promise1` ;
6. 将 Promise 中注册的回调函数放到微任务队列中,命名为 w1 ;
7. 执行async1函数,执行内部同步任务,输出`async1 start`;
8. 执行async2函数,执行async2内部同步任务,输出`async2`;
9. await任务后的console.log('async1 end')任务属于微任务,加入微任务队列,命名w2;
10. 执行同步任务,输出`script end`;
11. 同步任务执行完毕,从微任务队列取出任务 w1到主线程中,  输出`promise2`;
12. 从微任务队列取出任务 w2到主线程中,  输出`async1 end`;
13. 从宏任务队列中取出 time1到主线程中,输出`setTimeout`;

练习七、

const promise = new Promise((resolve, reject) => {
  console.log(1); //同步任务
    
  setTimeout(() => { //异步宏任务
    console.log('timerStart');  //异步宏任务中的同步任务
    resolve('success'); //异步微任务
    console.log('timerEnd'); //异步宏任务中的同步任务
  }, 0)

  console.log(2); //同步任务
});

promise.then((res) => {
  console.log(res); //异步微任务
});

console.log(4); //同步任务
//执行顺序:1,2,4,timerStart,timerEnd,success

练习八、

onsole.log('start')

const fn = () => {
  return new Promise((resolve, reject) => {
    console.log(1)
    resolve(2)
  })
}

console.log(3)

fn().then(res => {
  console.log(res)
})

console.log('end')

//执行顺序:start,3,1,end,2

六、vue中的nextTick

由于vue的更新机制是异步的,所以当数据修改之后,dom还停留在更新之前,此时想要获取更新后的dom,可以使用nextTick,表示的是下次dom更新循环结束后执行的回调。
应用场景:created 中获取dom可以使用nextTick

created() {
    // 使用nextTick可以在created生命周期获取dom节点
    this.$nextTick(() => {
        console.log(this.$refs.container);
    })
}

标签:异步,console,log,队列,EventLoop,任务,循环,事件,setTimeout
From: https://www.cnblogs.com/domin520Jian/p/17787697.html

相关文章

  • DataWhale DAY6 循环
    DataWhaleDAY6循环今天学习循环方面的知识。语法部分:https://www.cnblogs.com/hewo/p/17635277.html感觉python的for是真的像自然语言。range:​range(stop)#[0,stop)range(start,stop)#[start,stop)range(start,stop,step)#+=step关于while和for(其......
  • 【Java 进阶篇】JavaScript 事件详解
    在本篇博客中,我们将深入探讨JavaScript事件,这是网页交互的核心。我们将从什么是事件开始,然后逐步介绍事件的类型、如何注册事件、事件处理程序、事件对象以及事件冒泡等相关内容。最终,我们将提供大量的示例代码来帮助您更好地理解JavaScript事件。什么是事件?在Web开发中,事件是用户......
  • 分享我对C语言的分支和循环语句的理解
    ㈠我们都知道c语言的语句分为五类⒈表达式语句2.函数调用语句3.控制语句4.复合语句5.空语句而我们分享的分支和循环语句就属于控制语句在此之前我们先简单的介绍一下****控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句......
  • ConcurrentModificationException异常,for循环遍历时候, add或者remove减少集合的元素时
    ConcurrentModificationException异常一:ConcurrentModificationException异常:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。二:遍历list集合时删除元素出现的异常publicstaticvoidmain(String[]args){ArrayList<String>list=newArrayList<String>();......
  • 无涯教程-Clojure - 循环语句函数
    循环的特殊形式不同于"for"循环。循环的用法与let绑定相同,为了使循环发生,为循环指定的参数(arity)数必须与循环的绑定数一致。Loop-语法以下是循环语句的一般语法。loop[binding](condition(statement)(recur(binding)))以下是此循环的示意图。Loop-示例......
  • 项目中JSON序列化循环引用的问题
    已经很多次碰到循环引用的问题了之前碰到是在Spring中初始化过程中component之间存在循环引用怎么办 这次碰到,是在JSON序列话的时候碰到了循环引用导致的问题问题原因:在结构体中,为了实现方便数据的引用,在数据结构中加入了Parent节点,由于存在child节点,在生成json对象的时候就......
  • Chromium 消息循环和线程池详解
    Chromium中的多线程机制由base库提供,要理解Chromium中的多线程机制,首先要理解的概念就是 base::MessageLoop 和 base::TaskScheduler ,它们两个是Chromium多线程的基础1. MessageLoop详解base::MessageLoop 代表消息循环,它不会主动创建新的线程,默认情况下它使用当前......
  • 如何减少for循环层次
    背景数值上计算能量本征值往往采用矩阵对角化的方法。第一步是需要生成如下的矩阵:\[\braket{n|H|n^{\prime}}\]在计算这个矩阵的时候,只涉及两个变量:\(n\),\(n^{\prime}\)。因此两重for循环就可以解决这个问题。但是其他的情况下(如:系统中有两个粒子),需要生成的矩阵变成了:\[\bra......
  • BOM、DOM、事件监听
    BOM概念:BrowserObjectModel浏览器对象模型,允许JavaScript与浏览器对话,JavaScript将浏览器的各个组成部分封装为对象组成:Window:浏览器窗口对象Navigator:浏览器对象Screen:屏幕对象History:历史记录对象Location:地址栏对象 Window:浏览器窗口对......
  • 高级循环(附带小练习以及详细代码)
     1.无限循环概念:又叫死循环。循环一直停不下来。for格式:for(;;){System.out.println("循环执行一直在打印内容");}解释:初始化语句可以空着不写,表示循环之前不定义任何的控制变量。条件判断语句可以空着不写,如果不写,默认表示true,循环一直进行。条件控制语句可以空着不写,表示......