1、JS的执行顺序问题

  浏览器是按照从上到下的顺序解析页面,因此正常情况下,JavaScript脚本的执行顺序也是从上到下的,即页面上先出现的代码或先被引入的代码总是被先执行,即使是允许并行下载JavaScript文件时也是如此。
  Javascript语言的执行环境是"单线程"(single thread)。所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

2、JS的同步和异步概念  

  "同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

  "异步模式"非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。

  概念讲到这里已经足矣,关键是真正要理解,我们可以打开某一个网站的请求为例,F12打开调试模式:

客户端向服务器发出了三个请求,这三个ajax请求在时间调度上可以看到是相互重叠的,意味着异步的现象的存在,并不是执行完第一个请求再顺序去执行第二第三个,而是在启动了头一个的请求之后紧接着马上就去请求第二个,第三个请求也紧随其后,而请求耗时都是在运行中,意味着回调函数还未得以执行,浏览器已经另开一个线程去走下面的方法了。

我在项目中并未深入理解这个异步现象中,以至于在调试下方代码的时候出现了问题,具体见案例:

   var area;//area变量,执行Fn1()方法后才会有值
function Fn1(areaId){
debugger;
var url = 'admin/area/queryArea/'+areaId;
     //自己封装的一个ajax异步请求
$my.Get(url,function(data){
debugger;
area= data;
});
}
//打印area信息
function Fn2(area){
console.log(area);
}

执行方式1:

Fn1();
Fn2();

console控制台会报错,而异常原因还是是Fn2()中的area为undefined,按照我们正常的理解如果是顺序执行,明明Fn1执行在前,为什么会得到这样的结果呢,如果理解了Ajax的异步请求机制就很好理解,Fn1()执行,而后遇到ajax请求了,于是浏览器开另一个线程往下去顺序执行Fn2()方法,Fn2()方法中的变量是依赖Fn1()中的回调赋值函数,但是此时请求还未完成,因此会报异常信息。

正确的写法是如下:

  var area;//area变量,执行Fn1()方法后才会有值
function Fn1(areaId){
debugger;
var url = 'admin/area/queryArea/'+areaId;
     //自己封装的一个ajax异步请求
$my.Get(url,function(data){
debugger;
area= data;
Fn2(area);
});
} //打印area信息
function Fn2(area){
console.log(area);
}

  于是程序就能正常执行打印area信息了。实际上我们可以用调试模式来一步步跟代码:

  方法运行到了第一个debugger处,一步步调试进入到$my.Get()方法,这里封装一个简单的ajax的Get请求,默认为异步调用,但是方法却并未进入到下一个debugger中而是直接跑到了别的方法里头去了,在别处运行了一会儿程序才开始执行到第二个debugger中。
从中我们可以总结:假如有两个ajax请求f1()和f2(),f1发起了请求去往服务器,而f1中的方法并不会马上得到执行而是另开一个线程去执行f2,如果这两个方法没有依赖关系,而f1内的方法需要等到服务器响应后才会执行回调函数。在实际开发中,我们可能会有这样的关系,即f2()的执行需要用到f1()回调的内容,如果仅仅携程f1();f2();是会出现页面报错的,正确的写法则是:f1(f2());

var username;
function fn1(){
ajax("user/getNname",function (data) {
//the callback function
username=data.username;
//fn2()顺序执行才能拿到变量值
fn2();
})
//此处写fn2()还是拿不到变量值,将为undefined
//fn2()
}
function fn2(){
console.log(username);
}

  

  采用这种方式,我们把异步操作变成了同步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

  回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数,在js逻辑不是太复杂的情况下,采用这种方式是十分可行的。当然异步操作在面临复杂的业务场景还有更好的办法,这篇文章提炼总结的方法不错:js的四种异步编程方式。可以参考学习。

js中ajax异步问题的更多相关文章

  1. js中ajax异步导致的一些问题

    问题1:ajax默认是异步,所以在ajax中对外面定义的变量赋值,不能正确赋值 $("form").submit( var flag; $.ajax({ type: 'GET', ...

  2. js中ajax连接服务器open函数的另外两个默认参数get请求和默认异步(open的post方式send函数带参数)(post请求和get请求区别:get:快、简单 post:安全,量大,不缓存)(服务器同步和异步区别:同步:等待服务器响应当中浏览器不能做别的事情)(ajax和jquery一起用的)

    js中ajax连接服务器open函数的另外两个默认参数get请求和默认异步(open的post方式send函数带参数)(post请求和get请求区别:get:快.简单 post:安全,量大,不缓存)( ...

  3. JS中的异步以及事件轮询机制

    一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...

  4. js中的异步与同步,解决由异步引起的问题

    之前在项目中遇到过好多次因为异步引起的变量没有值,所以意识到了认识js中同步与异步机制的重要性 在单线程的js中,异步代码会被放入一个事件队列,等到所有其他代码执行后再执行,而不会阻塞线程. 下面是j ...

  5. JS中的异步

    Hello,日常更新的我“浪”回来了!!! JS中有三座高山:异步和单线程.作用域和闭包.原型原型链 今天“浪”的主题是JS中的异步和单线程的问题. 主要从这三个方面入手 一.什么是异步(与同步作比较 ...

  6. prototype.js 和 jQuery.js中 ajax 的使用

    这次还是prototype.js 和 jQuery.js冲突的问题,前面说到过解决办法http://www.cnblogs.com/Joanna-Yan/p/4836252.html,以及上网说的大部 ...

  7. js中ajax如何解决跨域请求

    js中ajax如何解决跨域请求,在讲这个问题之前先解释几个名词 1.跨域请求 所有的浏览器都是同源策略,这个策略能保证页面脚本资源和cookie安全 ,浏览器隔离了来自不同源的请求,防上跨域不安全的操 ...

  8. JS中的异步与回调

    问题的引出:在js中使用异步调用时,有可能会出现在异步的回调函数中设置调用之外的变量值,但在异步调用完成后去使用变量,却发现这些变量值并没有被成功设置的情况.如: google map中的地理编码,地 ...

  9. js中的异步[Important]

    js作为前端最主流的语言,主要处理页面显示变化(mutation)和异步(asynchronicity), js语言的基本要素和使用惯例的演化大都围绕着这两大主题,两者均值得总结和思考的主题, 这里先 ...

随机推荐

  1. 常用OLAP引擎

    l  presto Presto是Facebook开发的分布式大数据SQL查询引擎,专门进行快速数据分析. 特点: 可以将多个数据源的数据进行合并,可以跨越整个组织进行分析. 直接从HDFS读取数据, ...

  2. MySQL的IFNULL解决判空问题

    问题:mybatis返回的null类型数据消失,导致前端展示出错 思路:如果查询出的结果是空值,应当转换成空字符串.当然在前端也能进行判断,但要求后台实现这个功能. 解决方案: 使用如下方法查询: S ...

  3. C++11 初始化

    C++11 初始化 统一初始化语法        C++11新添加初始化列表 std::initializer_list<>类型,可以通过{}语法来构造初始化列表 .初始化列表是常数:一旦 ...

  4. 20个令人惊叹的深度学习应用(Demo+Paper+Code)

    20个令人惊叹的深度学习应用(Demo+Paper+Code) 从计算机视觉到自然语言处理,在过去的几年里,深度学习技术被应用到了数以百计的实际问题中.诸多案例也已经证明,深度学习能让工作比之前做得更 ...

  5. HGOI20180817 (NOIP模拟Day1 task)

    HGOI自测 初测:150=80+20+50 rank1~rank3(并列3个rank1,所以我是rank3 qwq) 今日分突然想简约 CF359A Table https://www.luogu. ...

  6. 洛谷P4198 楼房重建

    题意:给定序列,每次修改一个值,求前缀最大值的个数. 解:线段树经典应用. 每个节点维护最大值和该区间前缀最大值个数. 发现我们不用下传标记,只需要合并区间. 需要实现一个函数int ask([l r ...

  7. Codeforces 468C/469E 易错点

    #include <stdio.h> #include <stdlib.h> typedef long long ll; int main() { ll x=1e17; ll ...

  8. Vue实例的生命周期(钩子函数)

    Vue实例的生命钩子总共有10个 先上官方图: 下面时一个vue实例定义钩子函数的例子: var app=new Vue({ el:'#app', beforeCreate:function(){ c ...

  9. 说说Cookie和Session

    Session和Cookie在网站开发中是用来保存用户与后端服务器的交互状态.它们有各自的缺点和优点.而且,他们的优点和应用场景是对立的.   Cookie 完整地描述:当一个用户通过HTTP访问一个 ...

  10. SQL记录-PLSQL过程

    PL/SQL过程   子程序是一个程序单元/模块执行特定的任务.这些子程序被组合以形成更大的程序.这基本上是被称为“模块化设计”.子程序可以调用由另一个子程序或程序被称为调用程序. 子程序可以创建: ...