前言

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. 集智robot微信公众号开发笔记

    开发流程 公众号基本配置(首先得有公众平台账号) 在开发菜单的基本配置中填写好基本配置项 首先配置服务器地址.Token.和消息加密密钥(地址为开发者为微信验证留的接口.token可以随便填写,只要在 ...

  2. (数字IC)低功耗设计入门(八)——物理级低功耗设计&to be continued?

    前面学习了从系统级到门级的低功耗设计,现在简单地了解了一下物理级设计.由于物理级的低功耗设计与后端有关了,这里就不详细学习了.这里主要是学习了一些基本原则,在物理级,进行低功耗设计的基本原则是:    ...

  3. Zookeeper通过java创建、查看、修改、删除znode

    本章主要介绍zookeeper如何使用,其实通过zkCli.cmd我们是可以执行一些操作的:声明:参考及转自<http://www.blogjava.net/BucketLi/archive/2 ...

  4. pandas(七)数据规整化:清理、转换、合并、重塑之合并数据集

    pandas对象中的数据可以通过一些内置的方式进行合并: pandas.merge 可根据一个或多个键将不同的DataFrame中的行连接起来. pandas.concat可以沿着一条轴将多个对象堆叠 ...

  5. Java面试题——中级(上)

    List和Set比较,各自的子类比较 对比一:Arraylist与LinkedList的比较 1.ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高 ...

  6. Swing图层的应用——实现tooltip显示

    没有错是世纪前的swing. 在使用Swing的时候有个问题一直没有解决,就是Swing自带的tooltip不会跟随鼠标进行移动,而且移动到边界就会遮挡的问题.JCompoent有个createToo ...

  7. 学习css之选择器优先级

    相信每一位前端工作者最开始迷惑的地方便是界面展示为什么不符合预期效果呢,下面我来介绍一下可能引起上面结果的原因之一--css优先级. 我这里采用对照法来得出结论,代码如下: <style> ...

  8. Linux之centos系统常用命令总结

    1.查看系统版本 cat /etc/redhat-release 文件与目录操作 命令 解析 cd /home 进入 '/home' 目录 cd .. 返回上一级目录 cd ../.. 返回上两级目录 ...

  9. [LeetCode] Design Search Autocomplete System 设计搜索自动补全系统

    Design a search autocomplete system for a search engine. Users may input a sentence (at least one wo ...

  10. [LeetCode] Print Binary Tree 打印二叉树

    Print a binary tree in an m*n 2D string array following these rules: The row number m should be equa ...