0

0

为什么会有异步? 什么是事件队列?

PHP中文网

PHP中文网

发布时间:2017-06-21 13:26:27

|

3515人浏览过

|

来源于php中文网

原创

 引言

  相信所有学过 javascript 都知道它是一门单线程的语言,这也就意味着 js 无法进行多线程编程,但是 js 当中却有着无处不在的异步概念 。在初期许多人会把异步理解成类似多线程的编程模式,其实他们中有着很大的差别,要完全理解异步,就需要了解 js 的运行核心——事件循环(event loop)。在之前我对事件循环的认识也是一知半解的,直到我看了 philip roberts 的演讲 what the heck is the event loop anyway?,我才对事件循环有了一个全面的认识,所以我想写一篇介绍 js 事件循环的文章,以供大家学习和参考。

 一、为什么会有异步?

  为什么 JS 当中会有异步?我们想象一下,如果我们同步的执行一下代码会发生什么:

1 $.get(url, function(data) {2     //do something3 });

  在我们使用 ajax 进行通信的时候,我们都默认了它是异步的,但是如果我们设置其为同步执行,会发生什么?如果你自己写一个小的测试程序,将后台代码延迟5s你会发现浏览器会出现阻塞,直到 ajax 响应了之后才会正常运行。这便是异步模式要解决的首要问题,如何使浏览器非阻塞的运行任务。想象一下如果我们同步的执行 ajax 请求的话,我们的等待的时间是一个未知数,在网络通信中可能很快也可能很慢,也可能永远也不会响应,这也就会导致浏览器会阻塞在一个未知的任务上面,这也是我们不希望看到的。所以我们希望有一种方式能够异步的处理程序,我们并不需要关心一个 ajax 请求会在何时完成,甚至它可以永远不会响应,我们只需要知道在请求响应后该如何处理,并且在等待响应的这段时间内我们还可以做一些其他的工作。因此,便有了 JavaScript Event Loop。

 二、什么是事件队列?

  首先,我们先来看一段简单的代码:

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 1000);6 7 console.log("script end");

  你可以在这里查看结果:

   我们可以看到,首先,程序输出 'script start''script end',在大约1s之后输出了 'setTimeout' 。该程序的 'script end' 并没有等待1s之后输出,而是立即输出。这是因为 setTimeout 是一个异步的函数。意思也就是说当我们设置一个延迟函数的时候,当前脚本并不会阻塞,它只是会在浏览器的事件表中进行记录,程序会继续向下执行。当延迟的时间结束之后,事件表会将回调函数添加至事件队列(task queue)中,事件队列拿到了任务过后便将任务压入执行栈(stack)当中,执行栈执行任务,输出 'setTimeout'

  事件队列是一个存储着待执行任务的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。执行栈则是一个类似于函数调用栈的运行容器,当执行栈为空时,JS 引擎便检查事件队列,如果不为空的话,事件队列便将第一个任务压入执行栈中运行。

  现在,我们对上面的代码进行一点修改:

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 0);6 7 console.log("script end");

  将延迟时间设置为0,看看程序会以何种顺序输出?无论我们设置多少的延迟时间,'setTimeout' 总是会在 'script end' 之后输出。有些浏览器可能会有一个最小延迟时间,有的是 15ms,有的是 10ms,这个在很多书当中都有提到,这可能会给同学们造成一种错觉:由于程序运行速度很快,并且有最小延迟时间,所以 'setTimeout' 会在 'script end' 之后输出。现在让我们在稍微变一下,来消除你的错觉:

 1 console.log("script start"); 2  3 setTimeout(function () { 4     console.log("setTimeout"); 5 }, 0); 6  7 //具体数字不定,这取决于你的硬件配置和浏览器 8 for(var i = 0; i < 999999999; i ++){ 9     //do something10 }11 12 console.log("script end");

  你可以在这里查看结果:

   可以看出,无论后面我们做了多少延迟性的工作,'setTimeout' 总是会在 'script end' 之后输出。所以究竟发生了什么?这是因为 setTimeout 的回调函数只是会被添加至事件队列,而不是立即执行。由于当前的任务没有执行结束,所以 setTimeout 任务不会执行,直到输出了 'script end' 之后,当前任务执行完毕,执行栈为空,这时事件队列才会把 setTimeout 回调函数压入执行栈执行。

  执行栈则像是函数的调用栈,是一个树状的栈:

 三、事件队列有何作用?

  通过以上的 demo 相信同学们都会对事件队列和执行栈有了一个基本的认识,那么事件队列有何作用?最简单易懂的一点就是之前我们所提到的异步问题。由于 JS 是单线程的,同步执行任务会造成浏览器的阻塞,所以我们将 JS 分成一个又一个的任务,通过不停的循环来执行事件队列中的任务。这就使得当我们挂起某一个任务的时候可以去做一些其他的事情,而不需要等待这个任务执行完毕。所以事件循环的运行机制大致分为以下步骤:

  1.   检查事件队列是否为空,如果为空,则继续检查;如不为空,则执行 2;

  2.   取出事件队列的首部,压入执行栈;

    全诚外卖通外卖预订管理系统单店版
    全诚外卖通外卖预订管理系统单店版

    一、外卖通叫餐(预订)系统单店版是什么样的一个系统? 外卖通系列软件是针对非商品性买卖、有别于传统的商城系统的、外卖和预订为概念性的店铺管理系统,我们的口号就是:让所有的门店在网上安个家,以往的版本都是基于多用户性质的平台系统,而外卖通单店版是基于某个店铺的专业外卖预订管理系统,设计了外卖、预订、专题活动、小游戏、资讯、形象、点评、积分、相册等多种功能模块以适应商家办站的各种需求。这套系统可

    下载
  3.        执行任务;

  4.        检查执行栈,如果执行栈为空,则跳回第 1 步;如不为空,则继续检查;

  然而目前为止我们讨论的仅仅是 JS 引擎如何执行 JS 代码,现在我们结合 Web APIs 来讨论事件循环在当中扮演的角色。

  在开始我们讨论过 ajax 技术的异步性和同步性,通过事件循环机制,我们则不需要等待 ajax 响应之后再进行工作。我们则是设置一个回调函数,将 ajax 请求挂起,然后继续执行后面的代码,至于请求何时响应,对我们的程序不会有影响,甚至它可能永远也不响应,也不会使浏览器阻塞。而当响应成功了以后,浏览器的事件表则会将回调函数添加至事件队列中等待执行。事件监听器的回调函数也是一个任务,当我们注册了一个事件监听器时,浏览器事件表会进行登记,当我们触发事件时,事件表便将回调函数添加至事件队列当中。

  我们知道 DOM 操作会触发浏览器对文档进行渲染,如修改排版规则,修改背景颜色等等,那么这类操作是如何在浏览器当中奏效的?至此我们已经知道了事件循环是如何执行的,事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,不过在这间,浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能, jQuery 动画在底层均是使用 setTimeout 和 setInterval 来进行实现。想象一下如果我们同步的执行动画,那么我们不会看见任何渐变的效果,浏览器会在任务执行结束之后渲染窗口。反之我们使用异步的方法,浏览器会在每一个任务执行结束之后渲染窗口,这样我们就能看见动画的渐变效果了。

  考虑如下两种遍历方式:

 1 var arr = new Array(999); 2 arr.fill(1); 3 function asyncForEach(array, handler){ 4     var t = setInterval(function () { 5         if(array.length === 0){ 6             clearInterval(t); 7         }else { 8             handler(arr.shift()); 9         }10     }, 0);11 }12 13 //异步遍历14 asyncForEach(arr, function (value) {15     console.log(value);16 });17 18 //同步遍历19 arr.forEach(function (value, index, arr) {20     console.log(value);21 });

  经过测试,我们可以看出,采用同步遍历的方法,当数组长度上升到3位数的时候,便会出现阻塞,但是异步遍历却不会出现阻塞现象(除非数组长度非常大,那是因为计算机的内存空间不足)。这是因为同步遍历方法是一个单独的任务,这个任务会将所有的数组元素遍历一遍,然后才会开始下一个任务。而异步遍历的方法将每一次遍历拆分成一个单独的任务,一个任务只遍历一个数组元素,所以在每个任务之间,我们的浏览器可以进行渲染,所以我们不会看见阻塞的情况。下面这个 demo 演示了在异步遍历前后发生的事情:

 总结

  现在,相信你已经认识了 JavaScript 的真实面目了吧。 JavaScript 是一门单线程的语言,但是其事件循环的特性使得我们可以异步的执行程序。这些异步的程序也就是一个又一个独立的任务,这些任务包括了 setTimeout、setInterval、ajax、eventListener 等等。关于事件循环,我们需要记住以下几点:

  •   事件队列严格按照时间先后顺序将任务压入执行栈执行;

  •        当执行栈为空时,浏览器会一直不停的检查事件队列,如果不为空,则取出第一个任务;

  •        在每一个任务结束之后,浏览器会对页面进行渲染;

  本文 demo 放在 jsfiddle 上,如需转载,注明下出处就好了。若您发现本文有所纰漏,欢迎在评论区指出。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

178

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

35

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

79

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

2

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

4

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

8

2026.01.28

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

24

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

122

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

72

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号