海阔天空的云

我们在自己的世界里独自狂欢

0%

Ajax异步调用踩过的坑


写在前面

这些天在看朴灵的《node.js 深入浅出》 这本书,也对Javascript中的异步调用了有了更深入的一些理解。想起来我最早遇到的有关JS中的异步问题,应该就是在学习Ajax 的时候,当时对Ajax的概念甚至还不清楚,只是晓得Ajax能够用来接收后台API传过来的数据,后来也踩了几个坑,便有了想要梳理一番的想法,遂有此文吧。内容基础,倘若有不对之处,欢迎讨论。

Ajax的写法问题

曾经在网上看到有人发过帖子,去面试的时候面试官要求手写原生JS的Ajax格式,于是帖子下面纷纷在喷那位面试官。在这里,我说的Ajax 指是调用jQuery 库之后的Ajax写法了。

首先,我说一下我自己最常用的一种Ajax写法

    $.ajax({
       url: 'http://api.joind.in/v2.1/talks/10889',
       data: {
          format: 'json'
       },
       type: 'GET',
       async:true,
       error: function() {
          $('#info').html('<p>An error has occurred</p>');
       },
       dataType: 'jsonp',
       success: function(data) {
          var $title = $('<h1>').text(data.talks[0].talk_title);
          var $description = $('<p>').text(data.talks[0].talk_description);
          $('#info')
             .append($title)
             .append($description);
       },
       
    });

同步和异步

我曾经有一段时间弄不明白Ajax的同步和异步,就像是这个问题中 给出的写法那样写。

function foo() {
    var result;
    $.ajax({
        url: '...',
        type:'GET',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });
    return result;
}
var result = foo(); // It always ends up being `undefined`.
//use result continue

当发现不能实现自己想要的效果的时候,我通过网络搜索,也为了图省事,找到了这样一种写法。

    function foo() {
    var result;
    $.ajax({
        url: '...',
        async:false,
        type:'GET',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });
    return result;
}
var result = foo(); // have the result value,not undefined
//use result continue

上面的写法,相比于前面result为undefined 的情况,显然从结果上看是成功了,两种写法区别,只在于在$.ajax()函数内部添加了一个async:false 的条件,也就是将同步属性设置为了错,即此时调用Ajax之后实际上是走的同步流程。如果直接使用这样的写法,使用Chrome浏览器的话,打开浏览器的控制台后,调用Ajax的时候,控制台上会输出警告信息,不建议使用这种同步方法。然而,我当时以解决问题为目的,解决完这个问题很长一段时间,我也就没有再深入研究这个问题。

可以这样用

后来看了一些关于JS异步方面的博客和书籍,觉得既然JS的优势是他的异步,那么就应该充分发挥它的这种优势。然后在实际的项目中也发现,如果调用后台服务,数据量很大的话,使用同步机制的话,会造成页面的假卡死,而如果使用异步调用的话,我们可以在接收数据期间添加一些过渡动画来进行过渡,防止假死。在正式的项目中,当然也要避免使用async:false的情况了。

然后就有了下面的Ajax写法

    function foo() {
    var result;
    $.ajax({
        url: '...',
        type:'GET',
        success: function(response) {
            result = response;
            
           //use result continue
           console.log(result);
           alert(result)
        }
    });
   
}

上面的Ajax 写法,乍一看,好像并没有什么不同。但是事实上,将Async:false去掉了,也就是使用了默认的异步方法。另外,也是最重要的,将需要使用 result 的流程放到了success响应函数内部,即可以等到success响应之后立刻执行。仍然是异步处理的。

再进一步

我们可以把success 的响应函数单独提出来,下面的代码可以在我的codepen 找到,并无问题(jQuery 的ajax我倒是真的能纯手写了)。

let test =function(response){
  console.log(response)
}
$.ajax({
  url:'https://api.github.com/users',
  type:'GET',
  datatype:'JSON',
  success:test
})

封装

后来我又遇到了另外的一个问题,由于我最近的项目中使用了node.js+React+antd+webpack ,在项目中有一些函数需要在很多组件中频繁使用,因此我想到了可以对这些函数进行封装,这样不仅能够让每个组件中的代码量少很多,而且逻辑上也更加清晰了,还方便维护,很好。最初,我考虑到的是能不能使用React的一些特性来实现可复用函数,后来发现这种思路并不正确。实际上,node已经提供了引入导出的功能,我们完全可以将我们需要复用的文件写到一个模块里,然后只需要在合适的组件中引入模块即可。关于npm 的引入导出,可以看这篇文章

这个时候,就又用到了前面提到的调用github API例子中的方法了,下面以代码示例(我目前使用ES6 编写程序,示例也是ES6语法)。

let student=(name,age,func)=>{
let url = 'www.example.com/v1';
 $.ajax({
   type: 'POST',
   url:url,
   dataType: 'json',
   data: {
     'age':age, // 年龄
     'name': name, //姓名
   },
   async:true,
   success:func
 });
   }
export default student

这个时候当我们需要在我们的组件中使用上面的函数时,只需要在文件中引入student,并且传入相应的参数。

最后

正如我本文提到的那个示例,stackoverflow上那个关于这个这个话题的问题和相关回答给了我很大启发。有些so上面的大神也提出了使用ES6 的promise语法来实现,在这里我就不做讨论了。