详细:https://github.com/febobo/web-interview
5.事件循环
js是一种单线程语言,同一时间内只能做一件事情,为了避免单线程阻塞的方法就是事件循环。
在javascript当中,所有的任务都可以分为:
-
同步任务:按顺序执行的任务,一个任务的执行必须等待前一个任务的完成。同步任务一般会直接进入主线程执行。
-
异步任务:不按顺序执行的任务,可以在后台执行,不会阻塞代码的执行。比如:
ajax
网络请求,setTimeout,setinterval
定时函数等,Promise
,async/await
等。
同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就事件循环。
①宏任务与微任务
在JavaScirpt当中,宏任务(macrotask)和微任务(microtask)是异步任务的两种类型,它们有不同的执行时机和优先级。
宏任务(macrotask)
-
宏任务代表的是一组任务的执行,可以看作是每一个独立的、完整的任务。
-
常见的宏任务包括整体的代码块(scirpt)、setTimeout、setInterval、I/O操作、UI渲染等。
-
当执行栈中的任务执行完毕后,js运行时会检查是否有宏任务需要执行,然后选择一个宏任务执行,执行完后再次清空执行栈。
微任务(microtask)
-
相对于更“细粒度”的任务,执行时机位于当前任务执行结束后、下一个宏任务开始之前。
-
常见的微任务包括Promise的处理、MutationObserver变动观察器等。
-
微任务通常用于处理异步操作的结构,其优先级高于宏任务,及在当前宏任务执行完毕后,会优先执行微任务队列中的任务,直到微任务队列为空,才会执行下一个宏任务。
在执行顺序上,JavaScript 的事件循环(Event Loop)会先执行当前执行栈中的任务,然后检查是否有微任务需要执行,如果有,会依次执行微任务队列中的所有任务,直到微任务队列为空;最后再执行下一个宏任务。这样的循环过程不断重复,直到所有任务执行完毕。
console.log(1)setTimeout(()=>{console.log(2)
}, 0)new Promise((resolve, reject)=>{console.log('new Promise')resolve()
}).then(()=>{console.log('then')
})console.log(3)
如果按照上面流程图来分析代码,我们会得到下面的执行步骤:
1.console.log(1):首先会打印出 1,因为这是同步代码,会立即执行并输出 1。2.遇到 setTimeout,会将回调函数推入宏任务队列,等待当前执行栈清空后执行。3.遇到 Promise 对象,会立即执行 Promise 的构造函数中的代码,输出 new Promise。(1)紧接着,resolve() 会立即执行,表示 Promise 状态变为 resolved,进入 then 方法的回调链。(2)由于 Promise 的状态是 resolved,所以 then 方法中的回调函数会被放入微任务队列中等待执行。(3)接着会打印出 3,因为这是同步代码,会立即执行并输出 3。
4.此时宏任务执行完成,开始执行微任务队列执行 then 方法,输出 then5. 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,输出 2
结果是:1
=>'new Promise'
=> 3
=> 'then'
=> 2
详情可见Jake的博客:Tasks, microtasks, queues and schedules,这篇文章非常经典。