Ajax技术详解(上)

阅读本文仅需43分钟

在本文中,我们将从5个部分来对Ajax技术进行讲解:

  1. 背景知识介绍(包括所涉及的相关名词概念等)

  2. 服务端介绍(介绍三种搭建服务器的方法)

  3. 代码展示原生Ajax代码展示及讲解,jQuery-ajaxVue-resourceVue-axios微信小程序实现的案例讲解)

  4. 跨域问题(基础知识概述及跨域解决方案)

  5. 进阶Ajax现存问题分析ES6-PromiseGenerator的介绍)

背景知识介绍

01 - 背景知识引入

我们知道,在没有ajax技术之前,客户端若要与服务端进行数据交互,它的大概流程是:客户端发起一个http请求(也可能是其他的请求)然后处于等待状态,等到服务端将数据发送给客户端,客户端才会进行下一步操作,简单来讲,就是单纯的同步操作。然而,在现如今这个用户至上的年代,任何一个让用户不满的小操作都会导致巨大的流量丢失。

我来说两个简单的例子:

  1. 大家都发过微博吧,假设当前的这条微博处于正在发送的状态时,你还能继续刷微博查看其他内容么?很明显,完全可以,其实这就是一个ajax操作。

  2. 还是以新浪为例,打开新浪的登陆界面,填写相关信息之后,点击登陆,为什么没有出现那种整个页面都要被重新加载一遍的情况,而只是相关部分的改变,很简单,其实这也是一个ajax操作。

所以基于这些对用户不友好的操作,ajax技术横空出世。借用MDN上的一句话:

Ajax技术详解(上)_第1张图片

图1.1 Ajax-MDN介绍

02 - 相关基础概念

  • 同步与异步

看新闻联播的时候,经常会听到…人均GDP、国民经济同步稳幅增长,它的意思就是多件事一起增长,然而在我们所要讲解的这部分内容中,刚好是相反的,我以一张表格来说明它:

图1.2 同步、异步概念介绍

  • 缓存

首先说个例子,在web上多次打开腾讯视频,大家会发现,除了第一次,后面几次打开速度都挺快的,为什么呢,就是因为缓存,当客户端第一次请求一个网站时,客户端会把服务端给的数据做一份备份留在客户端,这样一来有两个明显的好处:

  1. 方便用户。当用户再次打开网站时,由于有了相关的缓存,所以打开速度就提升了

  2. 节省带宽。少发一次http请求(直接从缓存读取就行了)

Ajax技术详解(上)_第2张图片

图1.3 缓存相关

解释一下图片:这是我第二次请求腾讯视频控制台的结果,from memory cache是从内存中读取,速度更快,所以为0ms;from disk cache是从硬盘读取,由于涉及到IO操作,所以速度相比内存操作就慢点。

说了这么多,全是缓存的优点,接下来我们分析一下缓存的缺点。

我们都知道股票的趋势信息是实时更新的,如果不及时清理缓存的话,就会出现展示信息与当前真实情况不匹配的情况,从而造成股民由于信息获取不及时而导致一定程度的损失。

其实缓存在chrome中也不是特别严重,但是大家也都知道世界上有一个东西叫做IE6,我们还是得再某些情况下清一下缓存的,一句话说缓存的工作原理就是:它是根据url缓存的,换句话说,对于同一个url只缓存一次,所以我们只需要改变url就行了,怎么变呢?很简单,再请求的url后面加一个没有意义的时间戳即可。

HTML代码


    JS代码

    var obtn = document.getElementById("btn");
    var oul = document.getElementsByTagName("ul")[0];
    obtn.onclick=function(){
        ajax("index.txt",function(data){
            var real_data = JSON.parse(data);
            for (var i =0;i${i}${real_data[i].user}${real_data[i].password}`;
                    oul.appendChild(oli)
            }
        },function(errCode){
            alert(errCode)
        })
    }
    
    • 编码问题

    Ajax技术详解(上)_第3张图片

    由于计算机是美国人发明的,因此最早只有127个字符被编码到计算机里,也就是英文字母、数字和一些符号,这个编码表称为ASCII编码。

    然而世界上有这么多的国家,各国都有自己的标准,所以乱码问题是不可避免的,由此Unicode就应运而生了,它将所有语言都统一到一套编码里(编码通常是2个字节),这样就消除了乱码问题。

    无奈歪果仁们不了解中国文字的博大精深,对于简单的英文字母通常需要编码成一个字节,而对于汉字来说却需要3-6个字节。

    想象一个场景:

    如果要传输的文本中包含了大量的英文字母,若采用Unicode编码的话,肯定不会乱码,但肯定会造成空间的浪费。所以就有了可变长的UTF-8,它会把一个Unicode字符根据不同的数字大小编码成1-6个字节,从而节省空间。

    举个栗子:

    在Windows系统下,打开默认的记事本,输入联通两个字,保存之后,关闭记事本应用程序,重新打开,就会看到乱码。当你打开记事本编辑、保存、重新打开的过程中,编码的转化是一个什么过程呢?

    Ajax技术详解(上)_第4张图片

    图1.4 编码转化过程

    在下面的一个例子中,我会用ajax读取一个静态的文本文件,大家一定要注意html文件和文本文件的编码格式一定要一致(可以是utf-8或者gbk2312等),否则会出现乱码问题。

    • Get与Post区别

      • 使用区别

                Ajax技术详解(上)_第5张图片

    图1.5 Form Data数据项查看

    1. Get使用URL或Cookie传参,而Post将数据放在body中

    2. Get方式提交的数据有长度限制,一般在4k~10k,而post的数据则可以非常大,大概在2G。

    3. Post比Get安全(注意post只是相比get较为安全,更安全的还得看https,因为通过post方式提交的数据可以在控制台的Form Data中看到)

      • 终极区别

        1. 根据HTTP规范,Get用于信息获取,而且应该是安全的幂等的,所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,Get请求一般不应产生副作用,就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改、增加数据,不会影响资源的状态。

        2. 根据HTTP规范,Post表示可能修改服务器上的资源的请求。举个例子,读者自己发表新闻评论。

    服务端介绍

    01 - 本地采用wamp搭建

    启动wamp程序,看到电脑右下角显示如图片所示:

    图2.1 wamp启动图标

    即代表程序启动成功。

    在wamp的安装程序里按照图中路径找到文件并创建文件夹:

    Ajax技术详解(上)_第6张图片

    图2.2 wamp文件夹

    写一个ajax的小例子,来说明其中要注意的几个点:

    Ajax技术详解(上)_第7张图片

    图2.3 wamp文件内容

    文件介绍:

        ajax.js--->封装的ajax函数

        index.html--->前端显示界面 

        index.txt--->伪造的从后台获取的数据

    代码如下:

    HTML

    
    

      JS

      var obtn = document.getElementById("btn");
      var oul = document.getElementsByTagName("ul")[0];
      obtn.onclick=function(){
          ajax("index.txt",function(data){
              var real_data = JSON.parse(data);
              for (var i =0;i${i}${real_data[i].user}${real_data[i].password}`;
                  oul.appendChild(oli)
              }
          },function(errCode){
              alert(errCode)
          })
      }
      

      注意控制台访问方式:

      Ajax技术详解(上)_第8张图片

      图2.4 localhost访问方式

      Ajax技术详解(上)_第9张图片

      图2.5 127.0.0.1访问方式

      一句话说,既然是在服务器上访问,那么肯定不能使用本地路径了,此处使用127.0.0.1或localhost都行。

      02 - 到供应商购买(腾讯云或阿里云等云服务提供商)

      大家到阿里云或者腾讯云买一下就行,推荐买阿里云,虽然我用的是腾讯云部署完之后,通过ping-ip的方法检测通没通,这里给大家看下我的服务器:

      Ajax技术详解(上)_第10张图片

      图2.6 服务器界面展示

      我的ip你们就不用试了,因为为了防止一些不必要的攻击,我的ip处于禁ping状态,所以看下ping百度的网址的结果就行了:

      Ajax技术详解(上)_第11张图片

      图2.7 ping百度的结果展示

      其实这也是依托第三方搭建的平台,那我们能不能自己搭一个服务器呢,我们来看第三部分的内容。

      03 - Node.js搭建服务器

      实例引入:大家都知道,但凡传输一个较大的文件(2G以上),都是分块发送的,简单来说就是不会一下把这个文件传送过去,而是会将这个文件在发送端先进行切块,然后分块发送,到接收端拼起来就行,来我给大家证明这一点:

      代码如下:

      HTML

      用户名:
      密码:

      Node.js

      const http = require('http');
      http.createServer((req,res)=>{
          var str = '';
          var i = 0;
          req.on("data",(data)=>{
              console.log(`第${i++}次读取数据`);
              str += data;
          });
          req.on('end',function () {
              console.log('读取完成'+str);
          })
      }).listen(3652);
      

      显示结果:

      Ajax技术详解(上)_第12张图片

      图2.8 数据包分块发送

      几个注意的问题:

      • 出现端口被占的情况,两种方法:第一,关闭当前端口;第二,换个端口号。

      • 提交的文件内存一定要足够大,不然看不到效果。

      代码展示

      01 - 原生Ajax代码

      function ajax(url,fnSucc,fnFailed){
          if(window.XMLHttpRequest){
              var oAjax = new XMLHttpRequest()
          }else{
              var oAjax = new ActiveXObject("Microsoft.XMLHTTP")
          }
      //        alert(oAjax.readyState)    0    未初始化   还没有调用open()方法
          oAjax.open('GET',url,true)  //异步  99.9%都用
          oAjax.send()
      //        alert(oAjax.readyState)   1    载入   已调用send()方法,正在发送请求
          oAjax.onreadystatechange=function(){
              if(oAjax.readyState==4){ //浏览器和服务器进行到哪一步了     读取完成
                  if(oAjax.status==200){  //读取失败
      //                alert("成功:"+oAjax.responseText)
                      fnSucc(oAjax.responseText)
                  }else{
                      if(fnFailed){
                          fnFailed(oAjax.status)
                      }
                  }
      
      
              }
          }
      }
      

      几个注意的问题:

      • 变量与属性

      //console.log(a); //error
      console.log(window.a);//undefined
      

      在全局变量中访问一个未定义的变量会直接报错,访问一个未赋值的属性不会报错,只是undefined。

      在原生ajax代码中,第一个if判断的条件为什么要写成window.XMLHttpRequest而不是直接写成XMLHttpRequest呢?

      if(window.XMLHttpRequest){
          var oAjax = new XMLHttpRequest()
      }else{
          var oAjax = new ActiveXObject("Microsoft.XMLHTTP")
      }
      

      当兼容低版本浏览器时,因为不存在XMLHttpRequest,当用window.XMLHttpRequest写时,会将undefined转换为false,而不会报错使代码卡死。

      • IE6兼容

      XMLHttpRequest---高版本浏览器     

      ActiveXObject("Microsoft.XMLHTTP")---低版本浏览器(IE6)

      • 状态码

        • 状态值

      0: (未初始化)还没有调用send()方法。
      1: (载入)已经调用send()方法,正在派发请求。
      2: (载入完成)send()已经执行完成,已经接收到全部的响应内容。
      3: (交互)正在解析响应内容。
      4: (完成)响应内容已经解析完成,用户可以调用。
      
        • 状态码

      1**:请求收到,继续处理
      2**:操作成功收到,分析、接受
      3**:完成此请求必须进一步处理
      4**:请求包含一个错误语法或不能完成
      5**:服务器执行一个完全有效请求失败
      
      • eval函数

      通过ajax函数成功请求的数据类型是字符串类型,需要通过eval()JSON.parse()来解析,这里不推荐使用eval()函数,原因如下:

        • 使用eval()函数之后就不能debug调试了

        • 在非严格模式下没有自己的作用域

      02 - 代码示例

      • 请求静态文件并配合DOM显示

      HTML

      
      

        JS

        var obtn = document.getElementById("btn")
        var oul = document.getElementsByTagName("ul")[0];
        obtn.onclick=function(){
            ajax("index.txt",(data)=>{
                var real_data = JSON.parse(data);
                for(var i =0;i${i}${real_data[i].name}${real_data[i].password}`;
                    oul.appendChild(oli)
        
        
                }
            },(errCode)=>{
                alert(errCode)
            })
        }
        
        • jQuery-ajax代码示例

        $.ajax({    
            url: "index.txt",    //请求的url地址   
            dataType: "json",   //返回格式为json    
            async: true, //请求是否异步,默认为异步,这也是ajax重要特性    
            data: { "id": "value" },    //参数值    
            type: "GET",   //请求方式    
            beforeSend: function(request) {        
              //请求前的处理
              request.setRequestHeader("Content-type","application/json");
              request.setRequestHeader("Source","101");
              request.setRequestHeader("Token","aaw--wssw-ss...");
            },   
            success: function(data) {        
            //请求成功时处理    
            },   
            complete: function() {        
              //请求完成的处理    
            },    
            error: function() {        
              //请求出错处理    
            }
        });
        
        • Vue-resource代码示例

        HTML

        {{msg}}

        Vue

        new Vue({
            el:'#app',
            data:{
                msg:'数据正在后台快马加鞭处理呢~'
            },
            methods:{
                getData(){
                    this.$http.get('index.txt').then(response => {    
                        // get body data
                        this.msg = response.body;
                  }, response => {
                    // error callback
                    console.log("failed")
                  });
                }
            }
        })
        
        • Vue-axios

        HTML

        • {{post.title}}

          {{post.body}}

        • {{error.message}}

        Vue

        import axios from 'axios';
        export default {
          data() {
            return {
              posts: [],
              errors: []
            }
          },
          // Fetches posts when the component is created.
          created() {
            this.axios.get(`http://jsonplaceholder.typicode.com/posts`)
              .then(response => {
                // JSON responses are automatically parsed.
                this.posts = response.data
              })
              .catch(e => {
                this.errors.push(e)
              })
          }
          }
        
        // The Vue build version to load with the `import` command
        // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
        import Vue from 'vue'
        import App from './App'
        import axios from 'axios'
        import VueAxios from 'vue-axios'
        Vue.use(VueAxios,axios)
        
        
        /* eslint-disable no-new */
        new Vue({
          el: '#app',
          components: { App },
          template: ''
        })
        

        使用方法:

        Vue.axios.get(api).then((response) => {
          console.log(response.data)
        })
        
        
        this.axios.get(api).then((response) => {
          console.log(response.data)
        })
        
        
        this.$http.get(api).then((response) => {
          console.log(response.data)
        })
        
        • 微信小程序

        wx.request({
          url: 'test.php', // 仅为示例,并非真实的接口地址
          data: {
            x: '',
            y: ''
          },
          header: {
            'content-type': 'application/json' // 默认值
          },
          success(res) {
            console.log(res.data)
          }
        })
        

        跨域问题

        01 - 基础知识储备

        什么是跨域?

        跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。

        广义的跨域:

        • 资源跳转:A链接、重定向、表单提交

        • 资源嵌入:link、script、img、frame等dom标签,还有样式中background:url()、@font-face()等文件外链

        • 脚本请求:js发起的ajax请求、dom和js对象的跨域操作等

        其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

        同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指协议+域名+端口三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

        同源策略限制以下几种行为:

        • Cookie、LocalStorage 和 IndexDB 无法读取

        • DOM 和 Js对象无法获得

        • AJAX 请求不能发送

        常见跨域场景:

        URL 说明  是否允许通信

        1. http://www.domain.com/a.js

        2. http://www.domain.com/b.js  

        3. http://www.domain.com/lab/c.js

        同一域名,不同文件或路径 允许

        1. http://www.domain.com:8000/a.js

        2. http://www.domain.com/b.js   

        同一域名,不同端口 不允许

        1. http://www.domain.com/a.js

        2. https://www.domain.com/b.js   

        同一域名,不同协议 不允许

        1. http://www.domain.com/a.js

        2. http://192.168.4.12/b.js  

        域名和域名对应相同ip 不允许

        1. http://www.domain.com/a.js

        2. http://x.domain.com/b.js         

        3. http://domain.com/c.js

        主域相同,子域不同 不允许
        1. http://www.domain1.com/a.js

        2. http://www.domain2.com/b.js

        不同域名       不允许

        02 - 跨域解决方案

        • 通过jsonp跨域

        • 跨域资源共享(CORS)

        • WebSocket协议跨域

        • nodejs中间件代理跨域

        • document.domain + iframe跨域

        • location.hash + iframe

        • window.name + iframe跨域

        • postMessage跨域

        • nginx代理跨域

        跨域问题出错的样子:

        Failed to load http://open.iciba.com/dsapi/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8020' is therefore not allowed access.
        

        JSONP跨域解决方法:

        JSONP由两部分组成:回调函数和数据。

        回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。

        JSONP的理念就是,与服务端约定好了一个回调函数名,服务端接收到请求后,将返回一段Javascript,在这段Javascript代码中调用约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段Javascript代码后,就会执行这个回调函数,这时数据已经成功传输到客户端了。

        优点:不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;兼容性更好,在更古老的浏览器中都可以运行。

        缺点:只支持get请求

        • 最简单的跨域解决

        const urllib = require('url');
        const http = require("http");
        http.createServer((req, res) => {
           const data = {
               name: "xinzhuoyue",
               author:'shipudong'
           };
           const callback = urllib.parse(req.url, true).query.callback
           res.writeHead(200);
           //回调原页面上函数处理返回结果
           res.end(`${callback}(${JSON.stringify(data)})`);
        
        
        }).listen(3000, '127.0.0.1');
        console.log('服务器已启动...');
        
        //JSONP的理念就是,与服务端约定好了一个回调函数名,服务端接收到请求后,将返回一段Javascript,
        // 在这段Javascript代码中调用约定好的回掉函数,并且将数据作为参数进行传递.
        // 当网页接收到这段Javascript代码后,就会执行这个回掉函数,这时数据已经成功传输到客户端了.
        function jsonpCallback(data){
            console.log('跨域成功');
            console.log(data);
        }
        
        
        
        • 智能搜索

        HTML

        
        
        • 123

        CSS

        *{
         margin: 0;
         padding: 0;
        }
        input{
          width:300px;
          height:30px;
          border:1px solid lightgray;
          margin-top: 150px;
          margin-left: 200px;
          padding-left: 5px;
        }
        ul{
          width:307px;
          list-style: none;
          margin-left: 200px;
          display: none;
        }
        li{
          height:30px;
          border: 1px solid lightgray;
          line-height: 30px;
          padding-left: 5px;
        }
        

        JS

        function callbackD(response){
         var oUl = document.getElementById('ulList');
         var html='';
         if(response.s.length !=0){
           oUl.style.display='block';
           for(var i = 0;i'
           }
         }
         oUl.innerHTML = html;
        }
        
        
        window.onload = function(){
         //获取dom元素
         var oData = document.getElementById('inputSearch');
         var oUl = document.getElementById('ulList');
         //键盘按下后抬起触发事件(onkeyup)
         oData.onkeyup = function(){
           if(oData.value != ''){
             //创建标签(createElement)
             var script = document.createElement("script");
             //添加地址
             script.src='http://unionsug.baidu.com/su?wd='+this.value+'&p=3&cb=callbackD';
             //添加给body的(成为body包涵的孩子)
             document.body.appendChild(script);
           }else{
             oUl.style.display='none';
           }
         }
        };
        
        • 获取金山词霸开放平台的数据

        JSONP的理念就是,与服务端约定好了一个回调函数名,服务端接收到请求后,将返回一段Javascript,在这段Javascript代码中调用约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段Javascript代码后,就会执行这个回调函数,这时数据已经成功传输到客户端了。

        function jsonp(req){
         var script = document.createElement('script');
         var url = req.url + '?callback=' + req.callback.name;
         script.src = url;
         document.getElementsByTagName('head')[0].appendChild(script);
        }
        function hello(res){
         console.log(res)
        }
        jsonp({
         url : 'http://open.iciba.com/dsapi/',
         callback : hello
        });
        

        jQuery-ajax:

        $.ajax({
          url: '',
          type: 'get',
          dataType: 'jsonp',  // 请求方式为jsonp
          jsonpCallback: "onBack",    // 自定义回调函数名
          data: {}
        });
        

        Vue-resource:

        this.$http.jsonp(url ,{
          params: {},
          jsonp: 'onBack'
        }).then((res) => {
          console.log(res); 
        })
        

        CORS跨域:

        跨域问题,一直困扰我们非常久,特别是在前后端分离开发条件下,几乎一定会遇到跨域问题。

        跨域也有很多解决方案:JSONP,iframe,代理,反向代理等。

        如果是与后台一起开发过的小伙伴,肯定对这行代码感到熟悉:

        Access-Control-Allow-Origin: *
        

        其实,这就已经属于 CORS 跨域资源共享的内容了,但是,前端和后台交互的时候仍然存在很多很多问题。CORS 跨域资源共享就是指,在协议、域名、端口这三者一个或者多个不同的情况下,允许跨源获取服务器接口的内容。

        简单来说,CORS 只是在 HTTP 协议内部,新增了若干个首部字段,来制定跨域资源共享的实现规则 。

        const http = require('http');
        const server = http.createServer((request, response) => {
            if (request.url === '/') {
                if (request.method === 'GET') {
                    response.writeHead(200, {
                        'Access-Control-Allow-Origin': '*' // 关键代码
                    });
                    response.end("{name: 'shipudong', password: '123456'}");
                }
                if (request.method === 'POST') {
                    response.writeHead(200, {
                        'Access-Control-Allow-Origin': '*' // 关键代码
                    });
                    response.end("true");
                }
            }
            response.end('false');
        });
        server.listen(3000, () => {
            console.log('The server is running at http://localhost:3000');
        });
        
        $.ajax({
            url: "http://localhost:3000",
            type: "get",
            success: function (result) {
                console.log(result);
            },
            error: function (msg) {
                console.log(msg);
            }
        })
         // var data = { name: 'shipudong', password: '123456' };
         //
         // $.ajax({
         //     url: "http://localhost:3000",
         //     type: "POST",
         //     data: JSON.stringify(data),
         //     success: function (result) {
         //         console.log(result);
         //     },
         //     error: function (msg) {
         //         console.log(msg);
         //     }
         // })
        

        Nodejs作中间件的解决方法:

        app.all('*', function(req, res, next) {
            res.header("Access-Control-Allow-Origin", "*");
            res.header("Access-Control-Allow-Headers",
                "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
            res.header("Access-Control-Allow-Methods",
                "PUT,POST,GET,DELETE,OPTIONS");
            res.header("X-Powered-By",' 3.2.1')
            if(req.method=="OPTIONS") res.send(200);/*让options请求快速返回*/
            else  next();
        });
        

        webSocket实现跨域:

        websocket是一中全双工通信协议,该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

        03 - CORB问题

        Cross-Origin Read Blocking (CORB) blocked cross-origin response https://cm.l.qq.com/?Bid=5bdbc925ad7403a84d1459393b1ddc05&0.7613197047624425 with MIME type text/xml. See https://www.chromestatus.com/feature/5629709824032768 for more details.
        

        进阶

        01 - ajax的问题

        //回调多次
        $.ajax({
            url:xxx,
            dataType:'json',
            success(data){
                $.ajax({
                    url:xxx,
                    dataType:'json',
                    success(data1){
                        console.log("我成功啦~~")
                    },
                    error(){
                        alert('兄弟,不好意思,你错了')
                    }
                })
            },
            error(){
                alert('兄弟,不好意思,你错了')
            }
        })
        
        
        //带有逻辑的回调函数的ajax
        $.ajax({
            url:xxx,
            dataType:'json',
            success(userData){
                if(userData.type==='VIP'){
                    $.ajax({url:'getVIPItems',dataType:'json'},success(items){
                        //生成数据
                    },error(err){
                        alert("失败了")
                    })
                }else{
                    $.ajax({url:'getItems',dataType:'json'},success(items){
                        //生成数据
                    },error(err){
                        alert("失败了")
                    })
                }
            },
            error(){
                alert('兄弟,不好意思,你错了')
            }
        })
        

        存在问题:

        若需要请求多个接口,那就意味着需要多次发送ajax请求,那写出来的代码光缩进看的人都恶心了,这个问题被称之为回调地狱,若在回调函数中进行带逻辑的判断写出来的代码可读性和可维护性更是难以操控。

        02 - 解决方法

        • ES6-Promise

        //Promise
        Promise.all([
         $.ajax({url:xxx,dataType:'json'}),
         $.ajax({url:xxx,dataType:'json'}),
        ]).then(results=>{
         alert("完事了")
        },err=>{
         alert("兄弟,不好意思,又错了")
        })
        //带有逻辑的Promise
        Promise.all([
         $.ajax({url:'getUserData',dataType:'json'})
        ]).then(results=>{
         let userData=results[0];
        
        
         if(userData.type==='VIP'){
             Promise.all([
                 $.ajax({url:'getVIPItems',dataType:'json'})
             ]).then(results=>{
                 let items=results[0];
                 //读取成功时,执行生成列表、显示、渲染之类的事情
             },err=>{
                 alert('又错了')
             })
         }else{
             Promise.all([
                 $.ajax({url:'getItems',dataType:'json'})
             ]).then(results=>{
                 let items=results[0];
                 //读取成功时,执行生成列表、显示、渲染之类的事情
             },err=>{
                 alert('又错了')
             })
         }
        },err=>{
         alert("失败了")
        })
        

        实例讲解:请求两个静态文件(index.txt、json.txt)

        Coding-0.0.1-version

        let p1 = new Promise((resolve,reject)=>{
             console.log(resolve);
             $.ajax({
                 type:"get",
                 url:"index.txt",
                 async:false,
                 dataType:'json',
                 success(arr){
                     resolve(arr)
                 },
                 error(err){
                     reject(err)
                 }
             });
         })
         let p2 = new Promise((resolve,reject)=>{
             console.log(resolve);
             $.ajax({
                 type:"get",
                 url:"json.txt",
                 async:false,
                 dataType:'json',
                 success(arr){
                     resolve(arr)
                 },
                 error(err){
                     reject(err)
                 }
             });
         })
        //            
        //            p1.then((arr)=>{
        //                console.log(arr);
        //                p2.then((json_data)=>{
        //                    console.log(json_data);
        //                    p3.then()....
        //                    
        //                },(err)=>{
        //                    console.log(err)
        //                })
        //            },(err)=>{
        //                console.log(err)
        //            })
        Promise.all([
         p1,p2
        ]).then((arr)=>{
         console.log(arr);
         let [res1,res2] = arr;
         console.log(res1);
         console.log(res2);
        },(err)=>{
         console.log("至少有一个失败了~~")
        })
        

        Coding-0.0.2-version

        function createPromise(url){
             return    new Promise((resolve,reject)=>{
                 console.log(resolve);
                 $.ajax({
                     type:"get",
                     url,
                     async:false,
                     dataType:'json',
                     success(arr){
                         resolve(arr)
                     },
                     error(err){
                         reject(err)
                     }
                 });
             })
         }
        Promise.all([
         createPromise("index.txt"),
         createPromise("json.txt")
        ]).then((arr)=>{
         console.log(arr);
         let [res1,res2] = arr;
         console.log(res1);
         console.log(res2);
        },(err)=>{
         console.log("至少有一个失败了~~")
        })
        

        Coding-0.0.3-version

        // jQuery(高版本)的ajax的返回值是Promise对象
        Promise.all([
         $.ajax({url:'index.txt',dataType:'json'}),
         $.ajax({url:'json.txt',dataType:'json'}),
        ]).then((data)=>{
         let [res1,res2] = data;
         console.log(res1);
         console.log(res2)
        },(err)=>{
         console.log(err)
        })      
        
        • ES6-Generator

        //generator  
        runner(function *(){
        let data1=yield $.ajax({url:xxx,dataType:'json'});
        let data2=yield $.ajax({url:xxx,dataType:'json'});
        //完事
        });
        //带逻辑的的generator  该函数不能写为箭头函数的形式
        runner(function *(){
        let userData=$.ajax({url:'getUserData',dataType:'json'});
        if(userData.type==='VIP'){
           let items=$.ajax({url:'getVIPItems',dataType:'json'});
        }else{
           let items=$.ajax({url:'getItems',dataType:'json'});
        }
        //处理生成列表、渲染之类的事
        })
        

        Promise和generator的实用性:

            Promise---一次性可以读一堆数据

            Generator---带有逻辑性的读数据

        参考文献

        • jQuery-ajax:https://api.jquery.com/jQuery.ajax/

        • Vue-resource:https://github.com/pagekit/vue-resource

        • Vue-axios:https://www.npmjs.com/package/vue-axios

        • 回调地狱:https://www.jianshu.com/p/d31d2ecb4162

        • 异步解决方案:https://www.jianshu.com/p/50fd5c0b5908

        • Promise:https://es6.ruanyifeng.com/#docs/promise

        • Generator:https://es6.ruanyifeng.com/#docs/generator

        • async:https://es6.ruanyifeng.com/#docs/async

        - End -

        推荐阅读

        Ajax技术详解(上)_第13张图片

        网络视频会议 OR 网课,不存在的~

         

        Ajax技术详解(上)_第14张图片

        毕业设计(基于TensorFlow的深度学习与研究)之核心篇CNN-AlexNet详解

         

        Ajax技术详解(上)_第15张图片

        【东拼西凑】毕业设计之论文查重篇

         

        长按关注我

        Ajax技术详解(上)_第16张图片

        听,那是我haha大笑的声音

        你可能感兴趣的:(Ajax技术详解(上))