js与ajax中异步调用的简单理解

Posted 菜菜粥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js与ajax中异步调用的简单理解相关的知识,希望对你有一定的参考价值。

这个两段代码是在同一个js文件中

function connectServer(callback) 
    if (window.XMLHttpRequest) 
        xmlhttp = new XMLHttpRequest();
     else if (window.ActiveXObject) 
        xmlHttp = new ActiveXObject("Microsoft.XMLHttp");
     else return;
    xmlhttp.onreadystatechange = function() 
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) 
            callback(xmlhttp.responseText);
        
    
        // 这里是异步调用的模式
        // 这里的server是用node启动的服务端脚本,返回一个随机数
    xmlhttp.open('GET', '../server', true);
    xmlhttp.send();

下面这里是为一个button触发Click时间时请求一个随机数并写入内部的元素内容中

        buttons[i].onclick = function(i) 
            return function() 
                if (this.disabled) return;
                bannedButtons(this, buttons);
                this.childNodes[1].innerhtml = '...';
                this.childNodes[1].style.visibility = 'visible';
                connectServer(function(data) 
                    buttons[i].childNodes[1].innerHTML = data;
                    bannedButtons(null, buttons);
                    enableButtons(buttons);
                    checkInfoIsReady(buttons);
                );
            
        (i);

为什么connectServer(function(data) ….会是异步调用呢
先看些其他的

      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function() 
          return function () 
              console.log(i);
           
        ;
      

这种形式是事件触发的时候是 return内层函数 而不是我们想要的内存函数的执行结果

      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function() 
          console.log(i);
        (i);
      

上面的代码会在控制台输出0,1,2,3,4,五 行
这是因为function()(i)这样子写就相当于直接运行了这个函数 不用等事件触发

      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function(i) 
          console.log(i);
        (i);
      
      或
      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function(x) 
          console.log(x);
        (i);
      

发现结果同上 可以发现是i参数覆盖事件参数x变成i, 但是依旧不是事件触发的时候才执行

      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function() 
          return function() 
                console.log(i);
          
        (i);
      

理解js的垃圾回收机制对闭包有很大帮助, 记住一点js不会回收需要用到的东西 而闭包正式要使用大外部变量的东西,所以如果外部的变量没有被回收 那么你才有可能访问到你需要的值
这样子写闭包是不对的因为那个console中的i不是(i)因为根本就没有产生关系,是因为内存函数根本就没有接收到你传的参数i,所以console的i会自动向上层查找i变量,因为是事件触发异步的所以此时的i都是5 正确的写应该是这样子的 如下

      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function(i) 
          return function() 
                console.log(i);
          
        (i);
      
或
      for (var i = 0; i < 5; i++) 
        buttons[i].onclick = function(x) 
          return function() 
                console.log(x);
          
        (i);
      

这样子写就能保证i在往上寻找时,找到的是你用外部i赋给i变量的值

这里那么多的内容是帮助理解闭包的工作原理 只是加深对js的理解,从以上的例子可以看出, 这种事件触发 并不是说直接最后执行,而是按照顺序执行, 只是你不按闭包来写 就感觉是最后才运行 其实依旧是顺序的执行 只不过是异步的触发 注意闭包外层的那里的(i)加上,如果没有当你触发的时候就是返回内层函数,而我们希望的是要执行内层函数,更进一步 你可以看到内层的x其实是新建的变量存储i变量的值,并不是i变量 根据js垃圾回收机制,你能发现x是不会被回收的,也就是实际上有五个x变量
如果你不用闭包,那么你就只能使用到最后一个i变量其值是5,这也就是常见的循环中放事件的结果不和你的料想一致的原因

接着 我们再来看 为什么那里的ajax为异步的调用 首先明确一点在异步模式中没有return这种说法,并且都是采用回调函数callback处理异步 这两点很关键 , 不注意这两点 可能你就会掉到坑里

function connectServer(callback) 
    if (window.XMLHttpRequest) 
        xmlhttp = new XMLHttpRequest();
     else if (window.ActiveXObject) 
        xmlHttp = new ActiveXObject("Microsoft.XMLHttp");
     else return;
    xmlhttp.onreadystatechange = function() 
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) 
            callback(xmlhttp.responseText);
        
    
        // 这里是异步调用的模式
        // 这里的server是用node启动的服务端脚本,返回一个随机数
    xmlhttp.open('GET', '../server', true);
    xmlhttp.send();
        buttons[i].onclick = function(i) 
            return function() 
                if (this.disabled) return;
                bannedButtons(this, buttons);
                this.childNodes[1].innerHTML = '...';
                this.childNodes[1].style.visibility = 'visible';
                connectServer(function(data) 
                    buttons[i].childNodes[1].innerHTML = data;
                    bannedButtons(null, buttons);
                    enableButtons(buttons);
                    checkInfoIsReady(buttons);
                );
            
        (i);

分析如下
当用户触发button的点击事件时, 触发connectServer ()函数的执行这里首先是把一个匿名函数穿给callback这个参数,然后穿件一个XMLHttpRequest 对象,使用get方式异步的向服务器这里即是server脚本请求数据,接着这里的onreadystatechange会仍入一个队列(这个队列是专门来对异步程序存储的) 当轮到异步队列进行时这个函数得到执行, 也就是说如果你的程序有一个普通的死循环程序, 那么你不可能得到触发的结果, 因为普通代码的处理总是在异步队列的前面, 当服务端处理完成请求ok且可以响应了 然后执行里面的callback函数,刚才已经说了callback被函数赋值,而这里的data数据得到的就是服务端返回的数据, 接着就是对这个数据进行处理了, 这样就解决了异步调度的问题,因为后面的代码是依赖服务端返回的数据的,刚好这种回调函数的方法,解决了异步的问题

综上
再感悟下, 就是事件触发处理异步,也就是把利用垃圾回收机制,让所需要的变量不被回收,或者覆盖,从而实现,触发的时候是你所期望的值

再感悟下, 就是回调函数处理异步,也就是把要处理的东西,带到异步队列的感觉, 就是把要处理的东西,放到只有处理完才会进行的后面, 一般带延迟的东西,都是会抛到异步队列的, 所以你的代码也要放在异步中,防止再还没来得及得到数据时,先行处理完了

以上是关于js与ajax中异步调用的简单理解的主要内容,如果未能解决你的问题,请参考以下文章

最详细的原生js实现ajax的封装

html怎么实现ajax异步处理?

AJAX中的同步加载与异步加载

Jquery中的异步编程浅析 延期(deferred)的承诺(promise)

JS中事件的执行顺序和AJAX的异步

Vue.js 模板循环 - 异步 axios 调用