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

2 篇文章 0 订阅

这个两段代码是在同一个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数据得到的就是服务端返回的数据, 接着就是对这个数据进行处理了, 这样就解决了异步调度的问题,因为后面的代码是依赖服务端返回的数据的,刚好这种回调函数的方法,解决了异步的问题

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值