zoukankan      html  css  js  c++  java
  • js中ajax异步问题

    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的四种异步编程方式。可以参考学习。

  • 相关阅读:
    Spring MVC程序中得到静态资源文件css,js,图片文件的路径问题总结
    【转】MySQL数据丢失讨论
    【摘】 pt-query-digest工具一解
    【摘】Mysql备份还原数据库之mysqldump实例及参数详细说明
    【原】redis插件安装
    【摘】linux中fstab解说
    【原】mysql慢日志分析
    【原】带宽、流量单位换算关系
    【转】Linux查看内存大小和插槽
    【转】bind
  • 原文地址:https://www.cnblogs.com/tison/p/7886642.html
Copyright © 2011-2022 走看看