扒下JS的“底裤”之任务管理 :事件循环

简介: 扒下JS的“底裤”之任务管理 :事件循环

任务管理

JavaScript 语言的一大特点就是单线程,也就是说同一个时间只能处理一个任务。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,(事件循环)Event Loop 的方案应用而生。


JavaScript 处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。

  • 主线程中的任务执行完后,才执行任务队列中的任务
  • 有新的任务到来时会将其放入队列,采取先进先出的执行策略执行队列中的任务
  • 比如多个setTimeout同时到时间了,就要依次执行


任务包括 script(整体代码)、 setTimeout、setInterval、DOM 渲染、DOM 事件、Promise、XMLHTTPREQUEST 等


原理分析

宏任务与微任务

console.log(1) 
setTimeout(function(){
    console.log(5);
},0) 
Promise.resolve().then(function(){
    console.log(3);
}).then(function(){
    console.log(4);
})
console.log(2);
// 1 2 3 4 5
复制代码


执行过程

  1. 先执行console.log(1) 输出1
  2. 执行setTimeout宏任务 将function(){console.log(5);},0 放入宏任务队列。
  3. 执行Promise微任务将 console.log(3);放入微任务队列
  4. 执行同步代码console.log(2);输出2
  5. 主线程任务完成,微任务开始传入主进程
  6. 通过事件循环遍历微任务队列,将刚才放入的 Promise.then 微任务读取到主线程执行,然后输出 3
  7. 之后又执行 promse.then 产生新的微任务,并放入微任务队列
  8. 主线程任务执行完毕
  9. 再次事件循环遍历微任务队列,读取到 promise2 微任务放入主线程执行,然后输出4
  10. 主线程任务执行完毕
  11. 此时微任务队列已经无任务,然后从宏任务队列中读取到 setTimeout 任务并加入主线程,然后输出 5


脚本加载

引擎在执行任务时不会进行 DOM 渲染,所以如果把script 定义在前面,要先执行完任务后再渲染 DOM,建议将script 放在 BODY 结束标签前。


定时器

定时器会放入异步任务队列,也需要等待同步任务执行完成后执行。注意是宏任务消息队列

HTML 标准规定最小时间不能低于 4 毫秒,有些异步操作如 DOM 操作最低是 16 毫秒,总之把时间设置大些对性能更好。

定时器会在等待时间完成后将任务放入宏任务队列,只有当主进程的任务执行完毕之后才会将这个任务放入主进程执行。

setTimeout(()=>{
    console.log(2);
},1000)
function fn() {
    let i = 0;
    console.log(1);
    while(i < 99999999) {
        i++;
    }
}
fn(); 
// 1 2
复制代码


这里主线程先执行setTimeout,等待1s后将console.log(2);放入宏任务队列,等待期间同时执行同步代码 fn函数,输出1 。在等待1s后将console.log(2);放入宏任务队列,这时主进程的同步代码还未完成,宏任console.log(2);继续待在宏任务队列。等主进程同步代码执行完成之后,将console.log(2);从宏任务队列中弹出到主进程执行。输出2。

这是对定时器的说明,其他的异步操作如事件、XMLHTTPREQUEST 等逻辑是一样的


微任务

微任务一般由用户代码产生,微任务较宏任务执行优先级更高,Promise.then 是典型的微任务,实例化Promise 时执行器代码是同步的,然而实例函数 then 注册的回调函数是异步微任务的。

任务的执行顺序是同步任务、微任务、宏任务所以下面执行结果是 1、2、3、4

setTimeout(() => {
    console.log(4);
}, 0)
new Promise((resolve) => {
    console.log(1)
     resolve()
}).then((_) => console.log(3))
console.log(2);
// 1 2 3 4
复制代码


执行过程

  1. 执行脚本
  2. 执行setTimeout异步函数,将 console.log(4);放入宏任务队列
  3. 执行Promise函数,执行执行器函数代码(是同步的)直接输出1
  4. 执行Promise.then将console.log(3))放入微任务
  5. 执行同步代码console.log(2);输出2
  6. 主进程任务完成
  7. 将微任务console.log(3))放入主进程执行输出3
  8. 主进程任务完成
  9. 将宏任务 console.log(4);放入主进程执行输出4



目录
相关文章
|
30天前
|
存储 JavaScript 前端开发
深入理解JavaScript中的事件循环(Event Loop):机制与实现
【10月更文挑战第12天】深入理解JavaScript中的事件循环(Event Loop):机制与实现
75 3
|
2月前
|
JavaScript 前端开发 API
详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务
该文章详细讲解了队列数据结构在前端开发中的应用,并深入探讨了JavaScript的事件循环机制,区分了宏任务和微任务的执行顺序及其对前端性能的影响。
|
15天前
|
JavaScript 前端开发 开发者
JavaScript的事件循环
【10月更文挑战第27天】理解JavaScript的事件循环机制对于正确编写和理解JavaScript中的异步代码至关重要,它是JavaScript能够高效处理各种异步任务的关键所在。
29 1
|
30天前
|
前端开发 JavaScript
深入理解JavaScript中的事件循环(Event Loop):从原理到实践
【10月更文挑战第12天】 深入理解JavaScript中的事件循环(Event Loop):从原理到实践
35 1
|
1月前
|
Web App开发 JavaScript 前端开发
深入理解Node.js事件循环和异步编程模型
【10月更文挑战第9天】在JavaScript和Node.js中,事件循环和异步编程是实现高性能并发处理的基石。本文通过浅显易懂的语言和实际代码示例,带你一探究竟,了解事件循环的工作原理及其对Node.js异步编程的影响。从基础概念到实际应用,我们将一步步解锁Node.js背后的魔法,让你的后端开发技能更上一层楼!
|
1月前
|
设计模式 JavaScript API
Node.js 事件循环
10月更文挑战第3天
29 0
Node.js 事件循环
|
1月前
|
JavaScript 调度 数据库
深入浅出:Node.js中的异步编程与事件循环
【9月更文挑战第30天】在Node.js的世界里,理解异步编程和事件循环是掌握其核心的关键。本文将通过浅显易懂的语言和实际代码示例,带你探索Node.js如何处理并发请求,以及它是如何在幕后巧妙地调度任务的。我们将一起了解事件循环的各个阶段,并学会如何编写高效的异步代码,让你的应用程序运行得更加流畅。
56 10
|
1月前
|
JavaScript 前端开发 开发者
深入理解Node.js中的事件循环和异步编程
【9月更文挑战第31天】本文旨在揭示Node.js背后的强大动力——事件循环机制,并探讨其如何支撑起整个异步编程模型。我们将深入浅出地分析事件循环的工作原理,以及它如何影响应用程序的性能和稳定性。通过直观的例子,我们会展示如何在实际应用中利用事件循环来构建高性能、响应迅速的应用。此外,我们还会讨论如何避免常见的陷阱,确保代码既优雅又高效。无论你是Node.js的新手还是经验丰富的开发者,本篇文章都将为你提供宝贵的洞察和实用技巧。
63 6
|
2月前
|
JavaScript 开发者
深入理解Node.js事件循环及其在后端开发中的应用
【8月更文挑战第57天】本文将带你走进Node.js的事件循环机制,通过浅显易懂的语言和实例代码,揭示其背后的工作原理。我们将一起探索如何高效利用事件循环进行异步编程,提升后端应用的性能和响应速度。无论你是Node.js新手还是有一定经验的开发者,这篇文章都能给你带来新的启发和思考。
|
2月前
|
JavaScript API 数据库
深入理解Node.js事件循环及其在后端开发中的应用
【9月更文挑战第3天】本文将深入浅出地介绍Node.js的事件循环机制,探讨其非阻塞I/O模型和如何在后端开发中利用这一特性来处理高并发请求。通过实际的代码示例,我们将看到如何有效地使用异步操作来优化应用性能。文章旨在为读者揭示Node.js在后端开发中的核心优势和应用场景,帮助开发者更好地理解和运用事件循环来构建高性能的后端服务。