针对于Ajax嵌套调用以及Ajax数据依赖问题提出的解决方案,重点讲解,干货十足

一、引言

大家都知道,js是单线程的,但是支持异步,并且在js基础中我们也一起学习了js的执行机制。那么当我们在前台页面使用ajax请求后台服务器的时候,这个过程实际上也是异步操作,所以它们并没有先后顺序可言,我们更无法掌控。

在开发过程中,我们经常会遇到一个ajax依赖另一个ajax请求的数据,那么如果我们按平常的开发思路来进行ajax嵌套调用,那么势必会出现一系列问题,因为它两个ajax都是异步的,所以执行的先后顺序我们完全不知道,那么假如第二个ajax调用了,第一个没有调用,那么就证明第二个ajax没有拿到正儿八经的数据,所以势必会引起一系列的异常和粗错误。

那么如何解决这个问题?ECMAscript 6 原生提供了 Promise 对象,它代表了未来将要发生的事件,用来传递异步操作的消息。那么我们就可以通过Promise 对象来很好的解决ajax依赖嵌套的问题。

二、JavaScript Promise 对象

1、简介

ECMAscript 6 原生提供了 Promise 对象,是异步编程的一种解决方案,它代表了未来将要发生的事件,用来传递异步操作的消息,从它可以获取异步操作的消息。

2、作用

  • 可以避免多层异步调用嵌套问题(回调地狱)
  • Promise 对象提供了简洁的API,使得控制异步操作更加容易

3、特点

  1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

    状态描述
    pending初始状态,不是成功或失败状态。
    fulfilled意味着操作成功完成。
    rejected意味着操作失败。

    ​ 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

4、优缺点

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

5、创建

要想创建一个 promise 对象、可以使用 new 来调用 Promise 的构造器来进行实例化。

下面是创建 promise 的步骤:

var promise = new Promise(function(resolve, reject) {
    // 异步处理
    // 处理结束后、调用resolve 或 reject
});

Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。

示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let promise = new Promise((resolve, reject) => {
            // 异步处理:使用定时器来模拟异步任务
            setTimeout(() => {
                let flag = false;
                // 处理结束后、调用resolve(解析) 或 reject(拒绝)
                if (flag) {
                    //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
                    resolve("成功!");
                } else {
                    reject("失败!");
                }
            }, 200);
        })
       // 解析promise对象
       promise.then(
         		// 如果为成功状态
            (successMessage) => {
                //successMessage的值是上面调用resolve(...)方法传入的值.
                //successMessage参数不一定非要是字符串类型,这里只是举个例子
                console.log("调用成功!");
                console.log(successMessage);
            },
         		// 如果为失败失败状态
            (errorMessage)=>{
              	//errorMessage的值是上面调用reject(...)方法传入的值.
                console.log("调用失败!");
                console.log(errorMessage);
            } 
        )
    </script>
</body>

</html>

对于已经实例化过的 promise 对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。

promise.then() 是 promise 最为常用的方法。

promise.then(onFulfilled, onRejected)

promise简化了对error的处理,上面的代码我们也可以这样写:

promise.then(onFulfilled).catch(onRejected)

6、Promise调用Ajax

下面是一个用 Promise 对象实现的 Ajax 操作的例子。

resolve 方法和 reject 方法调用时,都带有参数。它们的参数会被传递给回调函数。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise调用原生的ajax请求</title>
</head>

<body>
    <script>
        // 创建Promise对象:解决异步事件嵌套依赖数据的问题
        var promise = new Promise((resolve, reject) => {
            // 1.创建xhr对象
            let xhr = new XMLHttpRequest();
            // 2.设置请求方式和URL地址
            xhr.open("GET", "http://127.0.0.1:8024/world");
            // 3.判断请求的次数和状态
            xhr.onreadystatechange = function() {
                    // 当请求的次数不为4就返回
                    if (xhr.readyState != 4) return
                        // 如果请求成功
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        // 将当前的服务器响应的数据以字符串的形式传给成功的函数
                        resolve(xhr.responseText);
                    } else {
                        reject("服务器异常!");
                    }
                }
                // 向服务器发起请求
            xhr.send();
        });

        // 判断Promise对象的最终状态
        promise.then(
            // 成功
            (success) => {
                console.log(success);
            },
            // 失败
            (error) => {
                console.log(error);
            }
        )
    </script>
</body>

</html>

7、Promise.then链式操作

链式操作,即返回一个promise对象,等待这个promise对象的状态,并将成功的结果,作为下一个ajax函数的参数,并再次调用函数,返回一个promise对象,这种行为操作就是链式操作,并有效的解决的开发中常见的ajax嵌套依赖数据的问题。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise链式操作</title>
</head>

<body>
    <script>
        // 可通用的原生ajax GET请求
        function reusable(url) {
            // 创建并返回Promise对象
            return new Promise((resolve, reject) => {
                // 1.创建xhr对象
                let xhr = new XMLHttpRequest();
                // 2.配置请求类型和请求URL地址
                xhr.open("GET", "http://127.0.0.1:8024" + url);
                // 3.判断请求的次数和状态
                xhr.onreadystatechange = function() {
                    // 如果请求的状态不是最后一次,那么就返回
                    if (xhr.readyState != 4) return;
                    // 如果状态是最后一次,并且成功
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        // 成功,并将参数传递
                        resolve(xhr.responseText);
                    } else {
                        // 失败,并将参数传递
                        reject("服务器响应失败!");
                    }
                };
                // 发起请求
                xhr.send();
            });
        }

        // 只要promise对象中的状态被确定,就立刻执行then函数,then函数就是等待promise对象的状态被确定,并且返回一个Promise实例对象
        reusable("/getdata").then(
            // 成功
            (success) => {
                console.log(success);
                // 调用下一个ajxa,并返回Promise实例对象
                return reusable("/addworld/success");
            },
            // 失败
            (error) => {
                console.log(error);
            }
            // 当上一个promise对象的状态被确定,再执行下一个then
        ).then((success) => {
            // 成功
            console.log(success);
            // 失败
        }).catch((error) => {
            console.log(error);
            // 成功与否都会执行
        }).finally(() => {
            console.log("结束");
        })
    </script>
</body>

</html>

8、then参数中的返回值

  1. 返回Promise实例对象

    返回的该实例对象会调用下一个 then

  2. 返回普通值

    返回的普通值会直接传递给下一个 then,通过 then 参数中函数的参数接收该值

9、Promise常用的API

A、实例方法
  • p.then() 得到异步任务的正确结果

  • p.catch() 获取异步任务异常信息

  • p.finally() 成功与否都会执行(尚且不是正式标准)

    示例:

    queryData().then(function(data){
        console.log(data);
      }).catch(function(data){
        console.log(data);
      }).finally(function(){
        console.log(‘finished');
      });
    
B、对象方法
  • Promise.all() 并发处理多个异步任务,所有任务都执行完成才能得到结果,执行结果由所有的p1,p2,p3共同决定,必须等所有的都完成,通过一个数组将所有的执行结果装起来给success

  • Promise.race() 并发处理多个异步任务,只要有一个任务完成就能得到结果,哪个先完成则success是谁

    示例

    //调用三次这个方法,并将路由传递,最后返回三个不同的promise对象        
    let p1 = reusable('/getworld');
    let p2 = reusable('/getdata');
    let p3 = reusable('/addworld');
    // 状态由三个一起来决定,只要有一个失败那么状态就是失败,否则全部成功状态才为成功,并返回一个数组
    Promise.all([p1, p2, p3]).then((success) => {
        console.log(success);
    }).catch((error) => {
        console.log(error);
    });
    // 谁先确定完状态,由谁来决定,成功与否都看第一个确定状态的对象,返回的就是哪个对象状态的结果
    Promise.race([p2, p3, p1, ]).then((success) => {
        console.log(success);
    }).catch((error) => {
        console.log(error);
    })
    

三、Fetch

1、简介

fetch是基于Promise实现的一种HTTP数据请求的方式,是XMLHttpRequest的一种替代方案。fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。

2、语法结构

  fetch(url,option).then(fn2)
            .then(fn3)
            ...
            .catch(fn)

示例

 let ajax = fetch("http://127.0.0.1:8024/updata", {
   		// ajax请求类型
       method: "PUT",
   		//传输数据,必须为string
       body: JSON.stringify({
          val: 20
       }),
   		//设置请求头 
       headers: {
         	// 传递json数据
          'Content-Type': "application/json",
       }
//这一步确定状态,并返回一个promise对象
}).then(
   			// 成功状态
      	(success) => {
          	// 返回数据类型为string
            return success.text();
        },
   			// 失败状态
        (error) => {
            console.log("服务器异常!");
        }
// 这一步根据状态解析promise对象
).then((val) => {
   		// 打印返回的结果
       console.log(val);
});

3、Fetch响应结果

响应数据格式

  • text(): 将返回体处理成字符串类型
  • json():返回结果和 JSON.parse(responseText)一样

4、Fetch 请求参数

(1)常用配置选项
  • method(String): HTTP请求方法,默认为GET (GET、POST、PUT、DELETE)
  • body(String): HTTP的请求参数
  • headers(Object): HTTP的请求头,默认为{}
(2)GET请求方式的参数传递
  1. 查询参数

    fetch(/abc?id=123).then(data=>{
        return data.text();
     }).then(ret=>{
        // 注意这里得到的才是最终的数据
        console.log(ret);
     });
    
  2. params参数

    fetch(/abc/123,{
        method: 'get'
     }).then(data=>{
        return data.text();
     }).then(ret=>{
        // 注意这里得到的才是最终的数据
        console.log(ret);
     });
    
(2)DELETE请求方式的参数传递
fetch('/abc/123' ,{
    method: 'delete'
 }).then(data=>{
    return data.text();
 }).then(ret=>{
    // 注意这里得到的才是最终的数据
    console.log(ret);
 });
(3)POST请求方式的参数传递
  1. 查询参数

     fetch('/books' ,{
        method: 'post’,
        body: 'uname=lisi&pwd=123,
        headers: {
           'Content-Type': 'application/x-www-form-urlencoded',
        }
     }).then(data=>{
        return data.text();
     }).then(ret=>{
        console.log(ret);
     });
    
  2. json字符串参数

     fetch('/books' ,{
        method: 'post',
        body: JSON.stringify({
           uname: 'lisi',
           age: 12
        })
        headers: {
           'Content-Type': 'application/json',
        }
     }).then(data=>{
        return data.text();
     }).then(ret=>{
        console.log(ret);
     });
    
(4)PUT请求方式的参数传递
 fetch(/books/123' ,{
    method: ‘put’,
    body: JSON.stringify({
       uname: 'lisi',
       age: 12
    })
    headers: {
       'Content-Type': 'application/json',
    }
 }).then(data=>{
    return data.text();
 }).then(ret=>{
    console.log(ret);
 });

5、Fetch 链式操作

// 使用fetch调用ajax,采用get方法,并返回一个Promise对象
let ajax = fetch("http://127.0.0.1:8024/getdata", {
       method: "GET"
  // 状态为成功
}).then(success => {
  	// 返回json类型的数据
    return success.json()
   // 状态为失败
}).catch(error => {
  	// 打印字符串
     console.log("服务器异常!")
});

// 获取Promise对象状态为成功的值
ajax.then(function(val) {
  	 // 成功以后调用下一个ajax
  let ajax = fetch(`http://127.0.0.1:8024/addworld/${val.hellow}`, {
         method: "GET"
 	//确定状态,并返回promise对象
  }).then(success => {
         return success.text()
         // 状态为失败
 	}).catch(error => {
         console.log("服务器异常!")
  // 解析promise对象,并返回结果
  }).then(val => {
        	console.log(val);
  	});
})

四、Axios

1、简介

Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post请求。说到get、post,大家应该第一时间想到的就是jQuery吧,毕竟前几年Jquery比较火的时候,大家都在用他。但是由于Vue、React等框架的出现,Jquery也不是那么吃香了。也正是Vue、React等框架的出现,促使了Axios轻量级库的出现,因为Vue等,不需要操作Dom,所以不需要引入Jquery.js了。

2、特性

  1. 可以在浏览器中发送 XMLHttpRequests
  2. 可以在 node.js 发送 http 请求
  3. 支持 Promise API
  4. 拦截请求和响应
  5. 转换请求数据和响应数据
  6. 能够取消请求
  7. 自动转换 JSON 数据
  8. 客户端支持保护安全免受 XSRF 攻击

3、使用场景

在特性里面已经有提到,浏览器发送请求,或者Node.js发送请求都可以用到Axios。像Vue、React、Node等项目就可以使用Axios,如果你的项目里面用了Jquery,此时就不需要多此一举了,jquery里面本身就可以发送请求。

4、基本用法

axios.type(URL:string,options:object).then(val=>{
  		// val为一个对象
     // data属性名称是固定的,用于获取后台响应的数据
     console.log(val.data)
})

注意

  1. axios.type(URL:string,options:object)不带then是有返回值,返回值为promise对象,因为axios支持支持 Promise
  2. 如果使用then,那么就是解析promise对象,就可以获取服务器响应的结果

5、链式操作

// 调用ajax,并解析promise对象
axios.get("http://127.0.0.1:8024/getdata").then(val => {
  // 将值作为参数进行洗一次的ajax请求
   axios.delete("http://127.0.0.1:8024/delete", {
       params: {
          id: val.data
       }
   // 解析promise对象
   }).then(val => {
     	// 拿到值
      console.log(val.data);
   })
})

6、参数传递

(1)GET传递参数
  1. 通过 URL 传递参数

    • 查询参数

      axios.get('/adata?id=123').then(ret=>{
         console.log(ret.data)
      })
      
    • 冒号参数

      axios.get('/adata/123').then(ret=>{
         console.log(ret.data)
      })
      
  2. 通过 params 选项传递参数

    axios.get('/adata',{
              params: {
                id: 123
              }
    }).then(ret=>{
       console.log(ret.data)
    })
    
(2)DELETE传递参数
  1. 通过 URL 传递参数

    • 查询参数

      axios.delete('/adata?id=123').then(ret=>{
         console.log(ret.data)
      })
      
    • 冒号参数

      axios.delete('/adata/123').then(ret=>{
         console.log(ret.data)
      })
      
  2. 通过 params 选项传递参数

    axios.delete('/adata',{
              params: {
                id: 123
              }
    }).then(ret=>{
       console.log(ret.data)
    })
    
(3)POST传递参数
  1. 通过选项传递参数(默认传递的是json格式的数据)

    axios.post('/adata',{
      uname: 'tom',
      pwd: 123
    }).then(ret=>{
      console.log(ret.data)
    })
    
  2. 通过 URLSearchParams 传递参数(application/x-www-form-urlencoded)

    //创建一个URLSearchParams对象
    const params = new URLSearchParams();
    // 向实例对象中添加数据
    params.append('param1', 'value1');
    params.append('param2', 'value2');
    // 调用ajax,并将URLSearchParams对象作为参数传递
    axios.post('/api/test', params).then(ret=>{
          console.log(ret.data)
    })
    
(4)PUT参数传递

通过选项传递参数(默认传递的是json格式的数据)

axios.put(/adata/123',{
  uname: 'tom',
  pwd: 123
}).then(ret=>{
  console.log(ret.data)
})

7、响应结果

响应结果的主要属性

  • data : 实际响应回来的数据
  • headers :响应头信息
  • status :响应状态码
  • statusText :响应状态信息

8、拦截器

(1)请求拦截器

功能:在请求发出之前设置一些信息

在这里插入图片描述

示例

//添加一个请求拦截器
axios.interceptors.request.use(function(config){
  //在请求发出之前进行一些信息设置
  return config;
},function(err){
// 处理响应的错误信息
});
(2)响应拦截器

功能:在获取数据之前对数据进行一系列的加工处理

在这里插入图片描述

示例

//添加一个响应拦截器
axios.interceptors.response.use(function(res){
	//在这里对返回的数据进行处理
  return res;
},function(err){
  // 处理响应的错误信息
})

五、async/await

1、简述
  1. async/await是ES7引入的新语法,可以更加方便的进行异步操作
  2. async 关键字用于函数上,async函数的返回值是Promise实例对象
  3. await 关键字用于 async 函数当中,await用于等待异步操作完成,并得到结果,这个结果可以是一个Promise实例对象
2、示例
  • 无参

    // 声明该函数为异步的
    async function books() {
      	// 使用await关键字等待ajax请求的数据,并返回promise对象
        const data = await axios.get("http://127.0.0.1:8024/getdata");
      	//并将promise对象返回
        return data;
    }
    // 函数调用在这里:成功以后解析promise对象
    books().then(val => {
      	// 取值
        console.log(val.data);
    })
    
  • 有参

    // 声明该函数为有参的异步
    async function addworld(id) {
      	// 将参数作为ajax请求的数据,并等待ajax请求结束,返回promise对象
        const data = await axios.get("http://127.0.0.1:8024/addworld/" + id);
      	// 返回promise对昂
        return data;
    }
    // 函数调用在这里,将参数传递:成功以后解析promise
    addworld(123).then(val => {
      	//取值
        console.log(val.data);
    })
    
3、链式操作
// 将这个函数设置为异步函数
async function queryData() {
    // 等待第一个异步事件执行并解析完成,拿到其值
    const info = await axios.get("http://127.0.0.1:8024/getworld").then(val => {
      			 // 将服务器响应的值返回
             return val.data;
		});
    // 将返回的结果从第个异步ajax中作为参数传递,并返回promise对象
		const ret = await axios.get(`http://127.0.0.1:8024/addworld/${info}`);
    // 最后返回一个promise对象
    return ret;
}
// 通过then解析promise对象拿到返回的值
queryData().then(val => {
  	// 获取其值
    console.log(val.data);
})

六、总结

以上均为Ajax嵌套调用以及Ajax数据依赖问题提出的解决方案,由繁到简,由难到易,但都是围绕javascript中的Promise对象展开的,如果说你看不懂Promise对象,那么抱歉,你掌握不了其他的方法。所以学习Promise对象的必须的,它起到了一个承上启下基础作用。

如果在日常开发中,我们没有使用jQuery库而是选择使用Vue这种虚拟DOM的框架,我还是非常建议大家去使用axios来实现ajax,因为它是简易上手的,并且是轻量的。或者你再搭配上async和await那么将是非常不错的开发选择。

如果还要不懂的朋友请评论,或者私信博主。或者在本篇文章中有总结的不对的地方,还请各位博友指点。好,今天就跟大家一起学习到这里,下一期将给大家带来这几天总结的Vue框架学习的整套API文档和经典案例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御弟謌謌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值