XMLHttpRequest对象
Ajax技术的核心是XMLHttpRequest对象(简称XHR),这是有微软率先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。
但因为IE的兼容性问题,这里对低版本的IE兼容性不做过多的描述。
XMLHttpRequest是一个构造函数,现在已经被各大浏览器厂商所支持,IE的话事支持IE7以上,XMLHttpRequest函数提供的方法有如下几个:
open: open有三个参数,第一个参数规定了当前请求的类型(get、post、put、delete、head),请求的统一资源定位符url,以及是同步还是异步的一个布尔值。
send: 在使用open方法创建请求以备发送之后,再调用send方法,就可以发送服务器进行这一次请求了。send方法主要是传递当前的请求是否需要给服务器传递数据。
tips:如果是get请求,则直接在open的url后面拼接上即可,但是不同浏览器对于url的长度是有限制的比如ie是2KB chrome是8KB大小,并且get请求是明文可见的,所以一般只是用来做查询数据的处理。在数据缓存方面因为get是用户查找数据,可以和不同的数据表进行联系,所以可以用于做服务器端缓存,但是post请求则不一样,post一般是用于传递数据进行数据表的增删改,并不合适于做缓存,前端培训并且post的数据传递在ajax中是不可见的,安全性方面也是优于get。
在send传递参数给服务器端之后,便会从服务器端获取服务器端数据的响应,判断服务器端是否成功接收到数据,并且返回响应的数据。
那么此时,会存在几个属性可以调用:
responseText:作为响应主体被返回的文本。
responseXML:如果响应的文件mime类型是text/xml或application/xml,这个属性包含着返回的xml文件。
status:当前响应的HTTP请求。
statusText:HTTP状态的说明。
那么,如何判断当前的响应状态呢,可以使用onreadystatechange这个事件处理程序来进行监听,同时配合readyState属性来搭配监听。
readyState有5个状态:
0尚未初始化。尚未调用open()方法。
1 启动。已经调用open方法,但还未使用send方法
2 发送。已经调用send方法,但尚未接收到响应数据
3 接受。已经接收到部分响应的数据
4 完成。已经全部接受到响应回来的数据,而且已经可以在客户端使用了。
在开发中经常会遇到在页面DOM加载完成之后向服务器端发起多个Ajax请求,比如有多个请求,后一个的请求需要上一次请求的返回结果。过去常规做法只能 callback 层层嵌套,这也就是多层ajax嵌套的回调地狱。
为了解决异步多层嵌套的问题,ES6推出了一个新的构造函数Promise。
Jquery $ajax
ajax是一个前后台配合的技术,它可以让javascript发送http请求与后台进行通信,从而获取后台返回的数据。ajax的原理是实例化xmlHttp对象,使用此对象与后台通信。在这里Jquery将他封装成了一个函数$.ajax(),并解决了多种浏览器的兼容性问题,我们可以直接使用这个方法。
它是对原生XHR的封装,还支持JSONP,非常方便;真的是用过的都说好。但是随着react,vue等前端框架的兴起,jquery早已不复当年之勇。很多情况下我们只需要使用ajax,但是却需要引入整个jquery,这非常的不合理,所以开始出现了新的插件比如axios等等。
ES6 Promise
Promise是一个构造函数对象本身有all、reject、resolve这几个方法,有fulfilled(已成功)/pengding(进行中)/rejected(已拒绝)这三种状态,原型上有then、catch等方法。
Promise可以接受一个参数那就是函数,并且这个函数有两个参数:resolve,reject,resolve和reject分别表示异步操作执行成功后的回调函数resolve和执行失败之后的回调函数reject。
在实例化promise对象之后,我们可以使用then和catch方法来进行成功后的回调和失败后的处理。then和catch都接受一个返回值,这个值包含了成功和失败后的状态和返回值,下面是执行then方法成功后获取的返回值,是一个对象包含了经典的xmlHttpRequest对象和status状态码,已经从后台获取的数据data。
如果想要更语义化一点可以使用另一个方法catch,当ajax出现异常的时候用于捕获promise的错误情况。
Promise.all
为了解决多层回调地狱的嵌套问题,promise提供了一个新的方法。Promise.all接受一个promise对象的数组作为参数,当这个数组里的所有对象都会resolve或者至少有一个reject状态的时候,它才会去调用then方法。
all方法会把回调函数放在promise对象数组中最慢返回的对象中去执行,也就是不必担心多层依赖的问题。
Promise.race
promise.race的字面量意思是竞赛,其用法和接受的参数和all一样,只不过race会把回调函数放在promise对象数组执行最快的回调中去执行。
扩展:Promise和axios在vue中的应用
在vue-cli脚手架中我们使用了axios进行ajax发送请求,axios是一个底层基于xmlHttpRequest的插件可用于node和浏览器端,因为需要使用promise对象来进行结果处理,所以使用的时候需要浏览器支持Es6。
axios支持get和post请求,对于jsonp请求作者认为是不安全的,所以在axios中并没有jsonp的方法,需要另外使用。
在上图中我们可以看到,在vue-cli脚手架中引用了axios之后创建了一个函数creatPromis用来重复创建promise对象,在对象里我们创建了一个axios函数,并且在then和catch中传入了resolve和reject用来分别执行成功和失败的回调。
我们创建了五个promise对象,通过一个数组把这五个对象传入其中并当参数传递给promise.all方法,当着五个promise都被正确执行的时候,我们会执行then里面的方法,否则会执行catch。这样不仅代码简洁还解决了传统意义上的多层ajax嵌套的回调地狱。
ES7 async
Promise虽然一定程度减少了回调嵌套,但是并不能完全消除嵌套。举个例子,对于多个依赖的promise来说,每产生一次依赖,就会增加一次嵌套。另外,采用promise的代码看起来依然是异步的,并不是同步。
async也是函数,所以具有普通函数该有的性质。不过形式上有几点不同:在定义async异步函数的时候,需要在function关键字钱加上async关键字,二是在函数内部可以使用await关键字,代表等待,表示后面跟随的结果当成异步操作并等待其完成。
async函数是generator函数的语法糖。它的定义方式有以下几种:
async是异步的意思,他本身也是是基于promise对象实现的,在获取到返回的值的时候我们需要用到原型链上的then方法去接受返回值,可以看出加了async关键字的函数timeout并没有阻碍js后面的执行。
async是基于promise对象实现的,所以会有resove和reject方法,这里我们传入一个flag参数,当为true的时候内部就会执行resove,但为false的时候就会执行reject。
await顾名思义有等待的意思,一旦在函数前加了await就代表,必须等当前的函数执行完毕,浏览器才会继续向下运行解析。在下图中我们使用了voteTest函数,并在内部执行了getVoteData函数,并在前面添加了关键字await,3秒钟过后voteTest下面的console开始了打印数据。
await就是用来等待Promise对象中resolve和reject这两个函数的执行的,并且将这两个函数传递的参数当作返回结果赋给变量,如同下图run函数中的代码示例那样。