浅析JS异步执行机制
前言
JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面。本文将针对JS异步执行机制进行一个简单的分析。
从一份代码讲起
下面是两个经典的JS定时执行函数,这两个函数的区别相信对JS有一定基础的同学是十分清楚的。timeout仅仅只会执行一次,而interval则会执行多次。
setTimeout(function (args) {
console.log('timeout')
}, 1000);
setInterval(function (args) {
console.log('interval')
}, 1000);
那么再看一份代码
setTimeout(function (args) {
console.log('timeout');
setTimeout(arguments.callee, 1000);
}, 1000);
setInterval(function (args) {
console.log('interval')
}, 1000);
这两份代码是否存在区别呢?在setTimeout中递归调用貌似和setInterval一样,但是实际上由于JS异步执行机制的问题,导致这两个函数存在着一定的差异。
如何理解JS异步执行机制
JS是单线程程序,从而避免了并发访问的一系列问题。但也正是由于单线程这样一个机制,导致JS的异步执行并不能按照传统的多线程方式进行异步执行,所有的异步时间要插入到同一个队列中,依次在主线程中执行。

这里有一张图片,可以比较好的解释JS的异步执行机制。
在浏览器中,一般情况下会存在三个线程,JS执行引擎,HTTP线程,事件触发线程。但是需要注意的是,所有的JS核心逻辑都需要在JS执行引擎线程中执行。
例如我们可以使用下面这样一段代码发送AJAX请求
var xmlReq = createXMLHTTP();//创建一个xmlhttprequest对象
function testAsynRequest() {
var url = "http://127.0.0.1:5000/";
xmlReq.open("post", url, true);
xmlReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlReq.onreadystatechange = function () {
if (xmlReq.readyState == 4) {
if (xmlReq.status == 200) {
var jsonData = eval('(' + xmlReq.responseText + ')');
alert(jsonData.message);
}
else if (xmlReq.status == 404) {
alert("Requested URL is not found.");
} else if (xmlReq.status == 403) {
alert("Access denied.");
} else {
alert("status is " + xmlReq.status);
}
}
};
xmlReq.send(null);
}
testAsynRequest();//1秒后调用回调函数
while (true) {
}
服务端代码
from flask import Flask
app = Flask(__name__)
@app.route('/', methods=['POST', 'GET'])
def print():
return 'hello world'
if __name__ == '__main__':
app.run()
这段代码是否会输出hello world呢?经过测验,发现并不会输出HelloWorld,浏览器会进入假死状态。造成这种情况的原因正是JS异步回调单线程的运行机制。在发送HTTP请求以后,HTTP请求会启动一个线程进行发送,收到响应以后会,事件触发线程会将响应事件加入到等待队列中,等待JS引擎空闲后执行。
但是由于while(true)导致JS引擎永远不存在空闲,从而导致响应事件一致无法触发。
重新思考
通过一个简单的AJAX DEMO,可以简单了解了JS时间执行的一个流程。那么针对上面的那张图片,和最开始提出的settimeout的问题,JS又是如何调度和处理的呢?
JS在定时器函数初始化以后就会开始执行定时任务,到达时间之后如果此时JS引擎空闲,则会直接执行定时任务,否则会将定时任务加入到等待队列中。
对于加入到等待队列中的任务来说,会在JS引擎空闲的时候再不断进行执行。因此如果此时引擎并非空闲,那么setTimeout会等待一段时间后才能执行。
对于setInterval来说,也是需要加入到等待队列中的,但是setInterval并不会因为加入到等待队列中而停止计时,此时如果到了第二个Interval,而第一个Interval还没有开始执行,那么此时队列中旧有存在两个Interval可能,如果这样累加下去,那么就可能会陷入大量Interval的累加,造成线程严重阻塞的问题,因此JS引擎做了一个轻度的优化,如果队列中有Interval,那么这个Interval不会加入队列。但是如果Interval已经pop出队列开始执行,那么Interval将会加入队列。
针对上面的分析,我们可以得出一个结论,相比于setTimeout函数递归调用,在JS中由于单线程的异步执行机制,setInterval执行的频率会更高。因为setTimeout在执行完成以后才会开始下一轮定时任务,但是setInterval是持续执行定时任务,尤其是在setTimeout里的任务执行时间较长的时候,setInterval和setTimeout会有比较明显的频率差异。
浅析JS异步执行机制的更多相关文章
- 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)
首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...
- JS 引擎执行机制
JS JS 是单线程语音 JS 的 Event Loop 是 JS 的执行机制.类似于 Android Handler 消息分发机制 JS 单线程 技术的出现都跟现实世界里的应用场景密切相关 JS 单 ...
- JS代码执行机制
JS代码从编译到执行 我们写出一段JS代码,JS的引擎并不是按照我们书写的顺序从上到下顺序编译并且执行的,首先是按照自己的规则对我们的代码先进行编译,然后从上到下执行编译的代码. 在全局作用域中,JS ...
- js 异步执行顺序
参考文章: js 异步执行顺序 1.js的执行顺序,先同步后异步 2.异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列 3.调用Promise 中的res ...
- js异步执行 按需加载 三种方式
js异步执行 按需加载 三种方式 第一种:函数引用 将所需加载方法放在匿名函数中传入 //第一种 函数引用 function loadScript(url,callback){ //创建一个js va ...
- #JS 异步处理机制的几种方式
Javascript语言的执行环境是"单线程"(single thread,就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推) ...
- JS异步执行之setTimeout 0的妙用
最近在工作中遇到一些问题,大致是关于js执行问题的.由于没搞清执行顺序,导致出现了一些奇怪的bug. 所以这里整理一些有关异步执行的知识(冰山一角角)... 大家都知道js是单线程的,执行起来是顺序的 ...
- js的执行机制
遇到一个问题,因为自己本身就是菜鸟的原因,弄懂了还是很高兴的. console.log(a) function a() { return "this is function" } ...
- 浅析js的执行顺序
javascript是一种描述型的脚本语言,是一种解析语言,由浏览器动态解析,不同种类的浏览器不同版本的浏览器对于js的解析有着微小的差别,不同浏览器的js解析引擎效率也有高低,下面来给大家分析一下j ...
随机推荐
- Django—中间件
中间件简介 什么是中间件 中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Django的输入和输出.每个中间件组件都负责做一些特定的功 ...
- 翻译:JVM虚拟机规范1.7中的运行时常量池部分(一)
原文链接: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4 Java Virtual Machine i ...
- window7下配置python2.7+tornado3.3开发环境
发现之前写太繁琐..这里分享下同学的方法 1,安装 Python 2.7.x 版本地址:https://www.python.org/downloads/release/python-278/2,安装 ...
- git的理论基础
GIT是目前世界上最先进最牛逼的分布式版本控制系统git维护的三棵树分别是工作区域.暂存区域.git仓库工作区域:就是你平时存放项目代码的地方暂存区域:用于临时存放你的改动,事实上它只是一个文件,保存 ...
- scrapy爬取豆瓣电影top250
# -*- coding: utf-8 -*- # scrapy爬取豆瓣电影top250 import scrapy from douban.items import DoubanItem class ...
- [LeetCode] Candy Crush 糖果消消乐
This question is about implementing a basic elimination algorithm for Candy Crush. Given a 2D intege ...
- [LeetCode] Permutation in String 字符串中的全排列
Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. I ...
- 关于 Rijndael 加密
MSDN https://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rijndaelmanaged(v=vs.110) ...
- Mysql中的常用函数:
Mysql中的常用函数: 1.字符串函数: (1).合并字符串 concat():// concat('M','y',"SQL",'5.5');== MySQL5.5//当传入的参 ...
- sprintf格式化字符串带来的注入隐患
原文链接:https://paper.seebug.org/386/ 摘要点关键知识点 <?php $input = addslashes("%1$' and 1=1#"); ...