前端开发JS:事件循环机制、调用栈以及任务队列

简介:

js里的事件循环机制十分有趣。从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的。

在之前,我只是简单地认为由于函数执行很快,setTimeout执行时间即便为0也不会马上输出,而是等待函数执行完后再输出。这只对了一半。

实际上其运行机制就是js中的事件循环机制,在这个循环机制中呢,又与call Stack和task queue有关。

前端开发

一、js事件循环机制

事件循环机制呢,简单点来说,就是在执行上下文的过程中,对函数的入栈和出栈。执行前函数先入栈,执行完后函数出栈。如若遇到了一些异步操作像回调函数以及ajax、setTimeout等,会先将他们交给浏览器的其他模块去执行,执行完后,会把回调函数放入到taskqueue中。当所有的call stack执行完后再开始执行task queue中的函数。

举一个简单的例子:

 
  1. console.log(1);  
  2. setTimeout(function() 

我们来看一下执行的内部过程

1. 执行第一句,放入call stack中,输出 1

js事件循环机制

2. 第一句出栈,执行第二句,由于是异步执行,交给其他模块。

js事件循环机制

3. 执行完后,将回调函数放入taskqueue中

js事件循环机制

4. 执行下一句,同第一步一样,将语句入栈并执行,输出3

js事件循环机制

5. 语句出栈,此时call stack空了。开始执行task queue任务,输出2

js事件循环机制

所以,输出结果是

js事件循环机制

与预想一致。

二、进阶

如果添加了Promise又如何工作呢?

我们知道,Promise的回调函数不是传入的,而是使用then来调用的。因此,Promise中定义的函数应该是马上执行的,then才是其回调函数,放入queue队列中。

还提到了一个重要的概念:

  • macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver

执行顺序:函数调用栈清空只剩全局执行上下文,然后开始执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次执行macro-task中的一个任务队列,执行完之后再执行所有的micro-task,就这样一直循环。

 
  1. (function test() {  
  2.     setTimeout(function() {console.log(4)}, 0);  
  3.     new Promise(function executor(resolve) {  
  4.         console.log(1);  
  5.         for( var i=0 ; i<10000 ; i++ ) {  
  6.             i == 9999 && resolve();  
  7.         }  
  8.         console.log(2);  
  9.     }).then(function() {  
  10.         console.log(5);  
  11.     });  
  12.     console.log(3);})() 

执行过程:

1. 遇到setTimeout,交给其他模块执行,执行完后回调放入macro-task中

2. 遇到Promise,立即执行里面的function,输出1。

3. 循环开始,遇到resolve(),修改Promise状态为fulfill。继续执行,输出2。

4. 遇到then,将回调放入micro-task中。

5. 继续执行,输出3。

6. call stack执行完毕了。开始执行micro-task中的回调函数,输出5。

7. micro-task执行完毕,开始执行macro-task中的回调函数,输出4。

8. 结束。


作者:谢军

来源:51CTO

相关文章
|
3天前
|
前端开发 JavaScript
前端ES5 | js —添加元素方法
前端ES5 | js —添加元素方法
|
3天前
|
JavaScript API 数据库
深入理解Node.js事件循环及其在后端开发中的应用
【9月更文挑战第3天】本文将深入浅出地介绍Node.js的事件循环机制,探讨其非阻塞I/O模型和如何在后端开发中利用这一特性来处理高并发请求。通过实际的代码示例,我们将看到如何有效地使用异步操作来优化应用性能。文章旨在为读者揭示Node.js在后端开发中的核心优势和应用场景,帮助开发者更好地理解和运用事件循环来构建高性能的后端服务。
|
7天前
|
JavaScript 前端开发 开发者
深入理解Node.js中的事件循环
【8月更文挑战第31天】在Node.js的世界里,“事件循环”是心脏,它驱动着异步操作的脉搏。本文将带你走进事件循环的核心,通过简单的例子揭示其神秘面纱。你将看到如何利用事件循环来处理并发任务,以及它是如何在背后默默支撑起你的后端应用。准备好,让我们一起探索这个让Node.js与众不同的特性吧!
|
7天前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
13 0
|
7天前
|
开发者 C# Android开发
明白吗?Xamarin与Native的终极对决:究竟哪种开发方式更适合您的项目需求,让我们一探究竟!
【8月更文挑战第31天】随着移动应用开发的普及,开发者面临多种技术选择。本文对比了跨平台解决方案Xamarin与原生开发方式的优势与劣势。Xamarin使用C#进行跨平台开发,代码复用率高,可大幅降低开发成本;但因基于抽象层,可能影响性能。原生开发则充分利用平台特性,提供最佳用户体验,但需维护多套代码库,增加工作量。开发者应根据项目需求、团队技能和预算综合考量,选择最适合的开发方式。
47 0
|
7天前
|
开发者 Android开发 iOS开发
Xamarin开发者的神器!揭秘你绝不能错过的插件和工具,让你的开发效率飞跃式提升
【8月更文挑战第31天】Xamarin.Forms 是一个强大的框架,让开发者通过单一共享代码库构建跨平台移动应用,支持 iOS、Android 和 Windows。使用 C# 和 XAML,它简化了多平台开发流程,保持一致的用户体验。本指南通过创建一个简单的 “HelloXamarin” 应用介绍 Xamarin.Forms 的基本功能和工作原理。首先配置 Visual Studio 开发环境,然后创建并运行一个包含标题、按钮和消息标签的示例应用,展示如何定义界面布局及处理按钮点击事件。这帮助开发者快速入门 Xamarin.Forms,提高跨平台应用开发效率。
20 0
|
7天前
|
JavaScript 中间件 开发者
深入浅出Node.js中间件机制
【8月更文挑战第31天】本文将带你领略Node.js中间件的奥秘,通过直观的案例分析,揭示其背后的设计哲学。你将学会如何运用中间件构建强大而灵活的后端应用,以及在面对复杂业务逻辑时如何保持代码的清晰与高效。
|
7天前
|
JavaScript 开发者
深入理解Node.js事件循环及其在后端开发中的应用
【8月更文挑战第31天】 本文将带你走进Node.js的事件循环机制,通过浅显易懂的语言和实例代码,揭示其背后的工作原理。我们将一起探索如何高效利用事件循环进行异步编程,提升后端应用的性能和响应速度。无论你是Node.js新手还是有一定经验的开发者,这篇文章都能给你带来新的启发和思考。
|
4月前
|
前端开发 JavaScript UED
深入理解JavaScript中的事件循环机制
JavaScript中的事件循环机制是其异步编程的核心,深入理解该机制对于开发高效、流畅的前端应用至关重要。本文将介绍事件循环的工作原理、常见的事件循环模型,以及如何利用这些知识解决前端开发中的常见问题。
|
4月前
|
开发框架 JavaScript 前端开发
JavaScript的事件循环机制是其非阻塞I/O的关键
【5月更文挑战第13天】JavaScript的事件循环机制是其非阻塞I/O的关键,由调用栈、事件队列和Web APIs构成。当异步操作完成,回调函数进入事件队列,待调用栈空时,事件循环取队列中的任务执行。在游戏开发中,事件循环驱动游戏循环更新,包括输入处理、游戏逻辑更新和渲染。示例代码展示了如何模拟游戏循环,实际开发中则常使用游戏框架进行抽象处理。
70 4
下一篇
DDNS