共计 2774 个字符,预计需要花费 7 分钟才能阅读完成。
在浏览器中,JavaScript 的执行是单线程的。如何在单线程中实现异步操作呢?答案就是事件循环。
事件循环(Event Loop)
浏览器通过事件循环来处理事件、用户交互、JS 代码执行、渲染、网络申请等。通常又两种事件循环,一种是 Window 事件循环,一种是 Worker 事件循环。因为它们外围的工作原理雷同,本文咱们仅仅探讨 Window 事件循环。
事件循环,首先是一个循环,每个循环周期会执行一些代码,一个循环周期被称为 tick。
while (eventLoop.waitForTask()) {eventLoop.processNextTask()
}
一个事件循环有一到多个工作队列。每个工作队列就是一个有序的工作列表。能够了解为一段要执行的代码,或者浏览器要执行的一个动作,比方发送事件、解析 HTML 等。
网页和浏览器自身的用户界面程序运行在雷同的线程中,共享雷同的事件循环。该线程就是主线程,它除了运行网页自身的代码之外,还负责收集和派发用户和其它事件,以及渲染和绘制网页内容等。而后,事件循环会驱动产生在浏览器中与用户交互无关的所有。
执行过程
简略的说,事件循环在每一个循环周期都会程序执行上面的步骤:
1. 抉择一个工作队列,从队列中取出最靠前(最老的)的工作。如果曾经没有工作了,则跳到第 3 步。
2. 执行取出的工作。
3. 从微工作(Micro Task)队列中取出微工作执行,晓得清空微工作队列。
4. 更新渲染(resize、scroll、动画等)
5. 返回第 1 步。
每一个工夫循环都有一个微工作队列。微工作队列与工作队列很像,不同的中央在于,每次循环周期只会执行工作队列中的一个工作,在这期间产生的任何工作都只能在下一个循环周期中能力得以执行。在执行前工作后,事件循环会一次执行微工作队列中的每一个微工作,直到微工作队列为空。也就是说,在微工作执行过程中新产生的微工作,也会在以后循环周期内失去执行。
不同的工作队列可能有不同的优先级。比方浏览器可能会将用户鼠标和键盘输入(用户交互)的工作都放在一个工作队列中,其余工作放到另外一个队列中。在每个循环周期中优先从用户交互队列中取出工作执行,来保障及时响应用户操作。
下图展现了一个事件循环周期的执行过程。
工作与微工作
一个工作能够简略的了解为一段要执行的 JavaScript 代码。比方当执行 <script> 标签中的代码时,一个工作会被增加到工作队列中。事件的回调函数、setTimeout、setInterval 的回调函数都会作为工作放到工作队列中。
每个事件循环周期,事件循环会从工作队列中取出一个最老的工作执行,其余工作要等到下一个事件循环周期才会执行。
微工作与工作没有实质的区别,只是因为被放入的微工作工作队列。事件循环在每个循环周期都会清空微工作队列中的工作。
在浏览器的实现中,
setTimeout
和setInterval
被放在了工作队列中,Promise
和 Mutation Observer API 被放在的微工作队列中。咱们也能够借助于 queueMicrotask()函数向微工作队列中增加工作。
执行异步代码
咱们有很多种形式来执行异步代码。
回调函数
咱们能够给一些事件增加监听,设置回调函数,从而在触发事件的时候执行函数。
buttonEl.addEventListener('click', () => {/* 点击响应 */});
回调函数会被放到工作队列中执行。
setTimeout 和 setInterval
这两个函数大家都十分相熟,setTimeout 会在指定的工夫之后执行回调函数,setInterval 会每个肯定的工夫执行一次回调函数。这些回调函数都是以工作的模式被增加到工作队列中。
setTimeout(() => {/* 1s 后执行 */}, 1000);
setInterval(() => {/* 每 500 ms 执行一次 */}, 500);
留神,这两个函数都接管一个工夫参数。然而理论运行的时候,事件循环并不会保障肯定依照这个工夫执行。
requestAnimationFrame
requestAnimationFrame 是一个非凡的工具函数,浏览器会在重绘页面之前调用这个函数设置的回调,容许咱们在页面重绘之前更新页面。
通过这个函数,咱们能够很好的在代码执行和运行设施的显示帧率(display frame rate)之间获得一个均衡。
如果,咱们通过 setInterval 来管制动画,因为每个事件循环周期执行的工夫不可控,而显示器的刷新频率是固定的(通常是 60Hz),因而如果咱们的动画执行过快,会呈现掉帧,执行过慢又会有卡顿景象。
requestAnimationFrame 会综合思考显示器的刷新频率和代码的执行,以达到动画可能顺滑执行。
requestAnimationFrame(() => {/* 执行一次 */})
// 每个事件循环都会思考执行
function alwaysRun() {
/* 函数逻辑 */
requestAnimationFrame(alwaysRun);
}
alwaysRun();
Promise 和 Async/await
Promise 和 Async/await 最终都会以 promise 的模式在代码中执行。
Promise 能够帮忙咱们治理有依赖关系的工作,同时能够肯定水平的防止回调函数的回调天堂问题。
new Promise((resolve, reject) => {/* 函数逻辑 */})
.then()
.then() // 链式调用
Async/await 是 Promise 的语法糖,能够帮忙咱们实现以同步的模式编写异步代码,解决了回调天堂问题。
async function task() {await someFunc();
await someOtherFunc();}
Promise 和 Async/await 的工作都是微工作,会被放到微工作队列中。
事件循环搞懂了,面试也不怕了,为一劳永逸我把 JS 相干的面试题整理出来,碰上有没搞懂的,面试题会了也行哈哈哈哈。
JS 题目
1. 请你谈谈 Cookie 的优缺点
2.Array.prototype.slice.call(arr,2) 办法的作用是:
4、简略说一下浏览器本地存储是怎么的
5. 原型 / 构造函数 / 实例
6. 原型链:
7. 执行上下文(EC)
8. 变量对象
9. 作用域
10. 闭包
11. 对象的拷贝
12.new 运算符的执行过程
13.instanceof 原理
14. 代码的复用
15. 继承
16. 类型转换
17. 类型判断
18. 模块化
19. 防抖与节流
20. 函数执行扭转 this
21.ES6/ES7
22.AST
23.babel 编译原理
24. 函数柯里化
25.get 申请传参长度的误区
篇幅起因列举了一部分题目,如何获取这份优质的材料呢?
疾速动手通道:点击这个,收费下载!诚意满满!!!
整顿不易,感觉有帮忙的敌人能够帮忙点赞分享反对一下小编谢谢~