前言:
从浏览器发展历史来看,JavaScript语言经历了几次重大革新。JavaScript诞生于1995年,当时,它的主要目的是处理以前由服务器端需要负责的一些没有输入验证操作。在JavaScript问世之前,必须把表单数据发送到服务器端才能确定用户是否没有填写某个必填域,是否输入了无效的值。
比如登录注册页面,现在咱们前端用js可以很方便的在接口调用之前,实现一些基本的表格校验,例如手机号格式、密码位数等,但是在js问世之前,这些只能是提交到后端,让后端接口告诉我们,你的手机号格式不对,请重新输入。
现在随着互联网技术、浏览器的发展,JavaScript作为一门脚本语言,支持绝大多数的web应用。前端和后端之间的通信,也经历了几次发展。CGI、ASP/PHP/JSP、iframe、src、Ajax、Fetch、JSONP、Web Socket、Promise,前端和后台有多种方式。本文重点介绍XMLHttpRequest、fetch、Promise。
1、XMLHttpRequest
(XHR)对象用于与服务器交互,是js的内置对象。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest
在AJAX 编程中被大量使用。
可以按如下步骤发起XMLHttpRequest请求。
1、实例化XMLHttpRequest对象。
2、调用实例化后的open方法。
3、调用send方法。
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var response = JSON.parse(xhr.responseText);
// Process the response data here
}
};
xhr.send();
该构造函数用于初始化一个 XMLHttpRequest
实例对象。
示例
下面示例使用工厂模式把定义 XMLHttpRequest 对象进行封装,这样只需要调用 creatXHR() 方法就可以返回一个 XMLHttpRequest 对象。
//创建XMLHttpRequest 对象
//参数:无
//返回值:XMLHttpRequest 对象
function createXHR () {
var XHR = [ //兼容不同浏览器和版本得创建函数数组
function () { return new XMLHttpRequest () },
function () { return new ActiveXObject ("Msxml2.XMLHTTP") },
function () { return new ActiveXObject ("Msxml3.XMLHTTP") },
function () { return new ActiveXObject ("Microsoft.XMLHTTP") }
];
var xhr = null;
//尝试调用函数,如果成功则返回XMLHttpRequest对象,否则继续尝试
for (var i = 0; i < XHR.length; i ++) {
try {
xhr = XHR[i]();
} catch(e) {
continue //如果发生异常,则继续下一个函数调用
}
break; //如果成功,则中止循环
}
return xhr; //返回对象实例
}
可以通过open方法初始化一个请求。
xhrReq.open(method, url);
xhrReq.open(method, url, async);
xhrReq.open(method, url, async, user);
xhrReq.open(method, url, async, user, password);
参数
method
要使用的 HTTP 方法,比如 GET
、POST
、PUT
、DELETE
、等。对于非 HTTP(S) URL 被忽略。
url
一个 DOMString 表示要向其发送请求的 URL。
async
可选
一个可选的布尔参数,表示是否异步执行操作,默认为 true
。如果值为 false
,send()
方法直到收到答复前不会返回。
user
可选
可选的用户名用于认证用途;默认为 null
。
password
可选
可选的密码用于认证用途,默认为 null
。
异步响应状态
在 JavaScript 中,使用 readyState 属性可以实时跟踪异步响应状态。当该属性值发生变化时,会触发 readystatechange 事件,调用绑定的回调函数。readyState 属性值说明如表所示。
返回值 | 说明 |
---|---|
0 | 未初始化。表示对象已经建立,但是尚未初始化,尚未调用 open() 方法 |
1 | 初始化。表示对象已经建立,尚未调用 send() 方法 |
2 | 发送数据。表示 send() 方法已经调用,但是当前的状态及 HTTP 头未知 |
3 | 数据传送中。已经接收部分数据,因为响应及 HTTP 头不安全,这时通过 responseBody 和 responseText 获取部分数据会出现错误 |
4 | 完成。数据接收完毕,此时可以通过 responseBody 和 responseText 获取完整的响应数据 |
如果 readyState 属性值为 4,则说明响应完毕,那么就可以安全的读取响应的数据。
常用的属性和方法还有
一个无符号长整型(unsigned long
)数字,表示该请求的最大请求时间(毫秒),若超出该时间,请求会自动终止。
如果请求已被发出,则立刻中止请求。
XMLHttpRequest.setRequestHeader()
设置 HTTP 请求标头的值。必须在 open() 之后、send() 之前调用 setRequestHeader()
方法。
特别注意:通过引入 fetch,XMLHttpRequest 在 ES 6 中被弃用。
2、fetch
XMLHttpRequest来完成ajax有些老而过时了。
fetch()能让我们完成类似 XMLHttpRequest (XHR) 提供的ajax功能。它们之间的主要区别是,Fetch API 使用了 Promises,它让接口更简单、简洁,避免了回调的复杂性,省去了使用复杂的 XMLHttpRequest API。
全局 fetch
函数是 web 请求和处理响应的简单方式,不使用 XMLHttpRequest。
语法:
fetch(url)
.then(...)
.catch(...)
示例:
使用fetch请求一张图片资源,并给页面上一个img标签赋值。
var myImage = document.querySelector('img');
var imgUrl = 'https://www.baidu.com/xxx.img'
fetch(myRequest).then(function(response) {
return response.blob();
}).then(function(response) {
var objectURL = URL.createObjectURL(response);
myImage.src = objectURL;
});
get请求:
fetch(url, {
method: 'get',
data: {},
responseType: 'blob',
}).then(async (res) => {
console.log(res)
})
post请求:
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: 'foo=bar'
})
.then(function (res) {
console.log(res);
})
.catch(function (error) {
console.log('failed', error);
});
res对象的几个方法:
- json():json方式解析数据------适合:一般数据
- arrayBuffer():把数据解析成二进制数组
- blob():不解析,原始二进制数据-----适合:图片、视频、音频等
- fromData():以表单方式解析数据
- text():以文本方式解析数据------适合:文本内容
res.json()、res.text()、res.blob()三种读取response返回数据的方法只能读取一次。再次读取会报错如下: Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream already read。
注意:当遇到网络错误时,fetch() 返回的 promise 会被 reject。fetch()发出请求以后,只有网络错误或者无法连接时,fetch()才会报错,其他情况(不管接口是超时还是code:1等错误处理)都不会报错:返回status:200,认为请求成功。 fetch还没有被所有浏览器兼容。
Promise:
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
一个 Promise
必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
基本用法 § ⇧
ES6 规定,Promise
对象是一个构造函数,用来生成Promise
实例。
下面代码创造了一个Promise
实例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
.then()
方法需要两个参数,第一个参数作为处理已兑现状态的回调函数,而第二个参数则作为处理已拒绝状态的回调函数。每一个 .then()
方法还会返回一个新生成的 promise 对象,这个对象可被用作链式调用,就像这样:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 300);
});
myPromise
.then(handleResolvedA, handleRejectedA)
.then(handleResolvedB, handleRejectedB)
.then(handleResolvedC, handleRejectedC);
Promise.all() § ⇧
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()
方法接受一个数组作为参数,p1
、p2
、p3
都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()
方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
Promise
也有一些缺点。首先,无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
总结:
以上就是js中常用的三种原生请求方式。原生的意思就是可以直接使用,而不用引入其他工具包。在项目中,也可以使用axios、jquery ajax、vue-request等封装好的请求库。