什么是事件循环?用来解决什么?

众所周知,JS是单线程的,但与其他主流编程语言一样,JS中也有异步的概念。其他语言中异步任务会利用自身的多线程新开一个线程去执行异步任务,而不会阻塞主线程中的任务,但在JS中如果直接执行异步任务就会阻塞这个线程(没办法,JS只有一个线程),所以事件循环的设计就是为了解决线程阻塞的问题,将异步任务收集起来集中处理(先将同步任务执行完毕)

事件循环的过程

JS执行代码为从上到下一行一行的执行,当遇到同步任务时,会交给主线程直接处理,遇到异步任务时则会将其挂起到一个任务队列中。

当处理完所有同步任务之后,则会从任务队列中读取任务,向上面那样执行此任务(如果为空则一直读取,直到里面有任务)

任务队列分为宏任务与微任务

执行顺序如下:

  1. 取出一个宏任务(最初整个JS代码块即为一个宏任务),开始执行
  2. 遇到异步任务,根据任务类型将其加入宏任务队列或微任务队列
  3. 此任务执行完毕后,从微任务队列出队一个微任务进行执行2(若为空则进行4
  4. 微任务队列清空后,执行渲染,如果宏任务队列不为空则进入1,开启新一轮的时间循环(如果为空,则重复检查宏任务队列)

记得画个图…

微任务与宏任务

为什么要区分呢?我想是为了效率,将所有微任务执行完后的状态进行渲染,要好过执行一个异步任务就渲染一次

示例


console.log('script start')

async function async1(){
	await async2()
	console.log('async1 end')
}

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

async1()

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

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

console.log('script end')

执行顺序如下:

  1. 遇到console.log,输出script start
  2. 遇到async1(),开始执行:
    1. await async2():await可以理解为隐式调用then,async2()直接执行(相当于new Promise()传入的函数)
    2. async2中输出async2 end
    3. await注册一个微任务,跳出此函数
  3. 执行setTimeout,注册一个宏任务
  4. 执行new Promise,输出Promise、then()注册一个微任务
  5. 执行console.log,输出script end
  6. 当前微任务队列为【await async2】【promise.then()】,所以await后输出async1 end,then()里输出promise1,在输出promise1后又注册一个then()随后执行输出promise2
  7. 微任务队列清空,执行宏任务【setTimeout】,输出setTimeout

关于async/await

async function async1(){
	await async2()
	console.log('async1 end')
}
async function async2(){
	console.log('async2 end')
}
async1()

// 上面的代码中关于async await可解释为Promise理解
function async1(){
	return new Promise((resolve, reject) => {
		//before await ...
		
		// start await
		let res // res用于接收await返回值(上面并没有接收await的返回值,这里写出来只是方便理解)
		async2().then(r => { // [1]
			// await 返回
			res = r;
			// await之后
			console.log('async1 end')
			resolve()
		})
	})
}
function async2(){
	return new Promise((resolve, reject) => {
		//before async2 return
		console.log('async2 end')
		resolve()
	})
}
// 如此结合上面的示例代码后,输出顺序就是
// script start
// async2 end -- 微任务加入[1]
// Promise    -- 微任务加入示例中的Promise.then,记为[2]
// script end
// async1 end -- [1]执行的结果
// promise1   -- [2]执行的结果,并加入后一个then记为[3]
// promise2   -- [3]执行的结果
// setTimeout -- 宏任务执行结果

node中的事件循环

… TODO

扩展

从event loop到async await来了解事件循环机制

面试题:说说事件循环机制(满分答案来了)

DORAKIKA
我是一个喜欢折腾的前端工程师,对一切新鲜事物充满好奇,希望我的文章能给你带来思考和帮助
👋我是DORAKIKA
分享作者『DORAKIKA』发表的文章『JS中的事件循环』https://blog.dorakika.cn/post/20221103/
© 请您在需要时著名本文内容来源信息,若在文末注明“参考、扩展”等字样涉及转载第三方内容,请您一同复制
如何fork自己的仓库
如何fork自己的仓库
fork自己的仓库可以实现在整体代码开源的同时,可以进行私有的额外开发
学习一手CSS中的clip-path
学习一手CSS中的clip-path
前几天要做一个过场动画,于是选择了使用clip-path的circle来进行元素裁剪实现,进而浅浅学习一下