前言

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异步执行机制的更多相关文章

  1. 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...

  2. JS 引擎执行机制

    JS JS 是单线程语音 JS 的 Event Loop 是 JS 的执行机制.类似于 Android Handler 消息分发机制 JS 单线程 技术的出现都跟现实世界里的应用场景密切相关 JS 单 ...

  3. JS代码执行机制

    JS代码从编译到执行 我们写出一段JS代码,JS的引擎并不是按照我们书写的顺序从上到下顺序编译并且执行的,首先是按照自己的规则对我们的代码先进行编译,然后从上到下执行编译的代码. 在全局作用域中,JS ...

  4. js 异步执行顺序

    参考文章: js 异步执行顺序   1.js的执行顺序,先同步后异步 2.异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列 3.调用Promise 中的res ...

  5. js异步执行 按需加载 三种方式

    js异步执行 按需加载 三种方式 第一种:函数引用 将所需加载方法放在匿名函数中传入 //第一种 函数引用 function loadScript(url,callback){ //创建一个js va ...

  6. #JS 异步处理机制的几种方式

    Javascript语言的执行环境是"单线程"(single thread,就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推) ...

  7. JS异步执行之setTimeout 0的妙用

    最近在工作中遇到一些问题,大致是关于js执行问题的.由于没搞清执行顺序,导致出现了一些奇怪的bug. 所以这里整理一些有关异步执行的知识(冰山一角角)... 大家都知道js是单线程的,执行起来是顺序的 ...

  8. js的执行机制

    遇到一个问题,因为自己本身就是菜鸟的原因,弄懂了还是很高兴的. console.log(a) function a() { return "this is function" } ...

  9. 浅析js的执行顺序

    javascript是一种描述型的脚本语言,是一种解析语言,由浏览器动态解析,不同种类的浏览器不同版本的浏览器对于js的解析有着微小的差别,不同浏览器的js解析引擎效率也有高低,下面来给大家分析一下j ...

随机推荐

  1. express学习(三)—— cookie和session

    express学习(三)-- cookie和session cookie存在浏览器中,最大只能保存4K数据,不安全 session存在服务器中,不能独立(先读取cookie再读取session),较安 ...

  2. uvalive 3602 DNA Consensus String

    https://vjudge.net/problem/UVALive-3602 题意: 给定m个长度均为n的DNA序列,求一个DNA序列,使得它到所有的DNA序列的汉明距离最短,若有多个解则输出字典序 ...

  3. Flume报 Space for commit to queue couldn't be acquired. Sinks are likely not keeping up with sources, or the buffer size is too tight

    报这个错误 需要一个是flume堆内存不够.还有一个就是把channel的容器调大 在channel加配置 type - 组件类型名称必须是memory capacity 100 存储在 Channe ...

  4. Testlink1.7.5安装部署

    Testlink1.7.5安装部署 1   下载安装wamp5:下载地址:http://www.duote.com/soft/56016.html wamp5是apache.mysql.PHP的集成环 ...

  5. WebGL文字渲染的那些问题

    THREE.js开发的应用运行在iphone5下发现有些时候会崩溃,跟了几天发现是因为Sprite太多频繁更新纹理占用显存导致的.通常解决纹理频繁更新问题就要用到one draw all方法,放到纹理 ...

  6. [LeetCode] Repeated String Match 重复字符串匹配

    Given two strings A and B, find the minimum number of times A has to be repeated such that B is a su ...

  7. [LeetCode] 01 Matrix 零一矩阵

    Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. The distance b ...

  8. MYSQL时间盲住

    mysql注释 -- ,-- -,#,--+等 常用的判断语句: ' and if(1=0,1, sleep(10)) # " and if(1=0,1, sleep(10)) --+ ) ...

  9. 用js来实现那些数据结构08(链表02-双向链表)

    其实无论在任何语言中,一种数据结构往往会有很多的延伸和变种以应对不同场景的需要.其实前面我们所学过的栈和队列也是可以用链表来实现的.有兴趣的小伙伴可以自己尝试着去实现以下. 有点跑题了...,我们还是 ...

  10. 实验吧_NSCTF web200&FALSE(代码审计)

    挺简单的一个代码审计,这里只要倒序解密就行了,这里给一下python版的wp import codecs import base64 strs = 'a1zLbgQsCESEIqRLwuQAyMwLy ...