Javascript事件循环模型之同步和异步

1、前言

众所周知Javascript是⼀⻔单线程语⾔,这也就决定了在Javascript中默认情况下同一个时间只能做一件事情,这个特性就造成了js的一些局限性,比如我们的页面需要发送HTTP请求从服务器获取数据时,就会出现等待数据返回之前页面假死的效果。由于js在同一个时间只能做一件事,就导致了页面的渲染和事件的执行在这个过程中无法同时进行。实际上,我们在开发中并没有遇见过这种情况,这是为什么呢?

为什么js是单线程的呢?这主要取决于它的用途,js作为浏览器的脚本语言,主要用途是处理与用户交互和操作DOM,这就决定了它只能是单线程。试想,如果js是多线程,在一个线程上对DOM节点进行修改,另一个线程对该DOM节点进行删除,此时浏览器一脸懵圈,该以哪个进程为准呢?于是为了避免这种情况,js从诞生起就决定了它是一门单线程的语言,这是它的核心特征。

2、同步(阻塞)和异步(非阻塞)

综上所述,在js中应该存在一种解决方案来处理单线程的不足。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

如果排队仅仅是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是空闲的,因为IO设备(输入输出设备)很慢(比如Ajax操作从服务器读取数据),不得不等着结果出来,再往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(阻塞),另一种是异步任务(非阻塞)。

同步(阻塞)

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,js会严格按照单线程从上到下、从左到右的方式执行代码逻辑,进行代码的解释和运行,所以不会出现先执行后面的代码在回头执行前面代码的情况。当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。

// 同步代码
const a = 1;
const b = 2;function fun1() {console.log(3);
}
function fun2() {console.log(4);
}
console.log(a);
fun1();
console.log(b);
fun2();// 输出
// 1
// 3
// 2
// 4

很容易可以看出,输出会依次输入1,3,2,4,因为代码是从上到下依次执行,先执行console.log(a),再执行fun1(),接着执行console.log(b),最后才执行fun2()。

再看下面的例子:

const x = 1;
const y = 2;
const t1 = new Date().getTime();
let t2 = new Date().getTime();
while(t2 - t1 < 3000){t2 = new Date().getTime();
}
//这段代码在输出结果之前网页会有一段时间的空白
console.log(x+y)

解释:上面的代码按照顺序从上往下执行时,当代码执行到第5行while语句时,就会进入一个持续的循环中。因为t1和t2的时间差很微小仅在毫秒内,所以while语句的t2-t1的值一定比3000小。每循环一次t2就会获取当前的时间戳,值就会发生变化,直到t2-t1>=3000时才会跳出当前的循环,也就是正好过了3秒的时间,最后在输出x+y的结果。那么这段代码实际上至少要执行3秒的时间,while循环不执行完,就不会执行后面的代码,这就导致了程序的阻塞,这也就是为什么将同步也称为阻塞的原因。

阻塞式运行代码,当遇到消耗时间的程序代码时,后面的代码都要等前面的代码执行完毕后才能继续执行,程序的执行顺序与任务的排列顺序是一致的、同步的。

异步(非阻塞)

单线程异步,异步是和同步对立的,异步模式的代码不会按照默认的顺序执行。js仍会严格按照单线程从上到下、从左到右的方式执行代码逻辑,进行代码的解释和运行,在解释代码时,遇到异步任务模式的代码,引擎会将当前的任务加入任务队列中(也叫做挂起),先不执行这段代码,继续向下执行后面的非异步代码。那么什么时候执行这些异步任务呢?直到全部的同步代码执行完毕后,进入任务队列的异步任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。所以,异步代码不会阻塞同步代码的运行,并且异步代码加入任务队列并不是进入新的线程同时执行,而是等待同步代码执行完毕后再执行。

异步的机制简单的概括为以下四步:

(1)所有同步任务都在主线程上执行,形成一个执行栈。

(2)主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件。

(3)一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断地重复上面的第三步。

function fun1() {console.log(1);
}
function fun2() {console.log(2);
}
function fun3() {console.log(3);
}
fun1();
setTimeout(function(){fun2();
},0);
fun3();// 输出
//1
//3
//2

这段代码运行时,当执行到setTimeout时,并不会直接执行函数内部的回调函数fun2(),而是会先将内部的函数加入任务队列,然后继续执行下面的fun3(),fun3()执行完毕后,任务队列通知主线程执行fun2()。

⾮阻塞式运⾏的代码,程序运⾏到该代码⽚段时,执⾏引擎会将程序保存到⼀个任务队列,等待所有同步代码全部执⾏完毕后,⾮阻塞式的代码会按照特定的执⾏顺序,分步执⾏。这就是单线程异步的特点。

3、总结

JavaScript的运⾏顺序就是完全单线程的异步模型:同步在前,异步在后。所有的异步任务都要等待当前所有的同步任务执⾏完毕之后才能执⾏。

我们把上面的例子改造如下:

const x = 1;
const y = 2;const t1 = new Date().getTime();
let t2 = new Date().getTime();setTimeout(() => {console.log('我是一个异步任务')
}, 1000)while(t2 - t1 < 3000){t2 = new Date().getTime();
}
console.log(x+y)// 输出
// 3
// 我是一个异步任务

运行这段代码可以看出,虽然我们给setTimeout的时间是1000毫秒,但是在while的阻塞3000毫秒的循环之后并没有等待1秒钟而是直接输出了结果“我是一个异步任务”,因为setTimout的时间计算是从setTimeout()这个函数执⾏时开始计算的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/645939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何快速上手Python开发以及学习资料推荐

学习Python并快速上手项目开发&#xff0c;需要制定一个系统的学习计划和实践策略。我的个人建议是&#xff1a; 基础知识入门&#xff1a; 安装Python环境&#xff1a;首先确保在电脑上安装了Python&#xff08;建议从官方网站python.org下载最新稳定版&#xff09;&#xff0…

逻辑回归中的损失函数梯度下降

一、引言 逻辑回归中的损失函数通常采用的是交叉熵损失函数&#xff08;cross-entropy loss function&#xff09;。在逻辑回归中&#xff0c;我们通常使用sigmoid函数将线性模型的输出转换为概率值&#xff0c;然后将这些概率值与实际标签进行比较&#xff0c;从而计算损失。 …

模型选择实战

我们现在可以通过多项式拟合来探索这些概念。 import math import numpy as np import torch from torch import nn from d2l import torch as d2l生成数据集 给定x&#xff0c;我们将使用以下三阶多项式来生成训练和测试数据的标签&#xff1a; max_degree 20 # 多项式的最…

如何系统的自学Python

1、官方文档 Python 的官方文档是最权威和详尽的学习资源。在官方文档中&#xff0c;你可以找到 Python 的语法规则、内置函数和模块、标准库等信息。如果你想深入学习 Python&#xff0c;官方文档是必不可少的参考资料。 Python 的官方文档分为两个版本&#xff0c;分别是 P…

前端面试题-(浏览器内核,CSS选择器优先级,盒子模型,CSS硬件加速,CSS扩展)

前端面试题-(浏览器内核&#xff0c;CSS选择器优先级&#xff0c;盒子模型&#xff0c;CSS硬件加速&#xff0c;CSS扩展&#xff09; 常见的浏览器内核CSS选择器优先级盒子模型CSS硬件加速CSS扩展 常见的浏览器内核 内核描述Trident(IE内核)主要用在window系统中的IE浏览器中&…

BTC交易模式 - UXTO - 工具整理

UXTO 相关工具分析 https://mempool.space/signet/ 测试网浏览器https://bitcoin.org/zh_CN/choose-your-wallet BTC钱包 正文链接&#xff1a;BTC交易模式 - UXTO

分布式锁实现(mysql,以及redis)以及分布式的概念(续)redsync包使用

道生一&#xff0c;一生二&#xff0c;二生三&#xff0c;三生万物 这张尽量结合上一章进行使用&#xff1a;上一章 这章主要是讲如何通过redis实现分布式锁的 redis实现 这里我用redis去实现&#xff1a; 技术&#xff1a;golang&#xff0c;redis&#xff0c;数据结构 …

使用Python的pygame库实现自动追踪目标的Snake游戏

和上一期不同的目标追踪入门不同的是&#xff0c;这期是自动追踪科学游戏&#xff0c;话不多说&#xff0c;321上链接 一、项目背景 Snake游戏是一款经典的游戏&#xff0c;玩家需要控制一条蛇在屏幕上移动&#xff0c;吃掉食物并避免撞到自己的身体或墙壁。传统的Snake游戏通常…

校园跑腿小程序源码系统+代取快递+食堂超市代买+跑腿 带完整的安装代码包以及搭建教程

随着移动互联网的普及&#xff0c;人们越来越依赖于手机应用来解决日常生活中的各种问题。特别是在校园内&#xff0c;由于快递点距离宿舍较远、食堂排队人数过多等情况&#xff0c;学生对于便捷、高效的服务需求愈发强烈。在此背景下&#xff0c;校园跑腿小程序源码系统应运而…

JAVA 学习 面试(九)Lambda表达式与泛型

Lambda表达式 // 使用 Lambda 表达式计算两个数的和 MathOperation addition (a, b) -> a b; // 调用 Lambda 表达式 int result addition.operation(5, 3); // MathOperation 是一个函数式接口&#xff0c;它包含一个抽象方法 operation&#xff0c;Lambda 表达式 (a, …

this.$copyText;vue-clipboard2作用;vue-clipboard2剪切板

1.安装 npm install --save vue-clipboard2 2.在main.js中引用 import Vue from vue import VueClipBoard from vue-clipboard2 Vue.use(VueClipBoard) 3.代码中使用 <button click"Copy">复制</button> Copy() { this.$copyText(this.value).then…

蓝桥杯备赛 week 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

css Media媒体查询常用属性

使用@media规则声明媒体查询,主要用于控制在不同的设备上显示不同的效果 媒体类型: screen 适用于电脑屏幕、平板电脑、智能手机等 print 适用于打印预览 特性 width 可视区域的宽度 orientation 视窗的旋转方向(横屏landscape,默认竖屏模式)。 运算符: and 并且 , 或…

Linux/Academy

Enumeration nmap 首先扫描目标端口对外开放情况 nmap -p- 10.10.10.215 -T4 发现对外开放了22,80,33060三个端口&#xff0c;端口详细信息如下 结果显示80端口运行着http&#xff0c;且给出了域名academy.htb&#xff0c;现将ip与域名写到/et/hosts中&#xff0c;然后从ht…

Mysql 文件导入与导出

i/o 一、导出(mysqldump)<一>、导出sql文件<二>、导出csv文件 二、导入(load)三、常见报错The Mysql server is running with the --secure-file-priv option so it cannot execute this statement 一、导出(mysqldump) <一>、导出sql文件 1、整库 mysqld…

【12.PWM输出】蓝桥杯嵌入式一周拿奖速成系列

系列文章目录 蓝桥杯嵌入式系列文章目录(更多此系列文章可见) PWM输出 系列文章目录一、STM32CUBEMX配置二、项目代码1.main.c --> PWMOutputProcess 总结 一、STM32CUBEMX配置 STM32CUBEMX PA6 ->TIM16_CH1; PA7-> TIM17_CH1 预分频设置为79,自动重装载设置999PWM输…

PyQtGraph 之PlotCurveItem 详解

PyQtGraph 之PlotCurveItem 详解 PlotCurveItem 是 PyQtGraph 中用于显示曲线的图形项。以下是 PlotCurveItem 的主要参数和属性&#xff1a; 创建 PlotCurveItem 对象 import pyqtgraph as pg# 创建一个 PlotCurveItem curve pg.PlotCurveItem()常用的参数和属性 setData(…

资源管理核心考点梳理

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 PMP - 资源管理核心考点梳理 资源管理包括人力资源和实物资源管理。学习的重点是人力资源的管理&#xff0c;这一章是考试的重点章节&#xff0c;在新考纲中&#xff0c;“人”这一模块在题目种的比例是42%。 01 …

在uvm中,以svi结尾和sv结尾文件的区别

在UVM&#xff08;Universal Verification Methodology&#xff09;中&#xff0c;.sv和.svi文件扩展名通常是SystemVerilog文件的标准扩展名。它们都用来标识SystemVerilog源代码文件。然而&#xff0c;不同项目或团队可能会采用不同的命名约定来区分不同类型的SystemVerilog文…

14.块参照的旋转(BlockReference)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET FrameWork4.5、ObjectArx 2016 64bit、Entity Framework 6. 在排水管网数据的编图时&#xff0c;时常会遇见针对雨水箅等进行旋转。由于数据存储在数据库内&#xff0c;通过CAD自带的旋转功能只能变更图面而无法…