XMLHttpRequest对象是当今所有AJAX和Web 2.0应用程序的技术基础
示例代码:
const url = "https://c-ssl.duitang.com/uploads/item/202002/24/20200224112148_nknod.jpg" //要渲染的图片链接
const img = document.getElementById('img');
req.open('GET', url); //以GET方式请求url资源
req.responseType = 'blob';
req.onload = function () { //设定一个回调函数
if (req.status === 200) {
img.src = url; //设置img标签的src为指定url
}
else {
console.log('failed');
}
}
req.onerror = function () {
console.log("network error");
}
req.send();
注意,我们这里引入XHR对象的状态码与状态值,用来确认我们对象已经运行到了哪一步状态
readyState,是指运行AJAX所经过的几种状态,无论访问是否成功都将响应的步骤,可以理解成AJAX运行步骤,使用“ajax.readyState”获得
status,是指无论AJAX访问是否成功,由HTTP协议根据所提交的信息,服务器返回的HTTP头信息代码,使用“ajax.status”获得
也就是说可以把status理解为http状态码的映射,readyState是xhr对象进入不同步骤的状态,如初始化
0:初始化,XMLHttpRequest对象还没有完成初始化
1:载入,XMLHttpRequest对象开始发送请求
2:载入完成,XMLHttpRequest对象的请求发送完成
3:解析,XMLHttpRequest对象开始读取服务器的响应
4:完成,XMLHttpRequest对象读取服务器响应结束
const req = new XMLHttpRequest();
首先创建XMLHttpRequest对象
对象创建完成后,
const url = "https://c-ssl.duitang.com/uploads/item/202002/24/20200224112148_nknod.jpg"
这里我放了一张02的美图哈哈哈哈!
const img = document.getElementById('img');
我们在index.html中设置了img id = ‘img’以供js抓取
req.open('GET', url);
param1:请求方法 param2:请求资源的url
注意!!!open方法不会发送请求!
req.responseType = 'blob'
这里很重要哦!!!好多bug例如资源无法解析,数据格式不对就是响应类型没有设置好
responseType值的类型可为如下
值 | 数据类型 |
---|---|
‘’ | DOMString (这个是默认类型) |
arraybuffer | ArrayBuffer对象 |
blob | Blob对象 |
document | Document对象 |
json | JavaScript object, parsed from a JSON string returned by the server |
text | DOMString (video流可能用这个) |
Binary large Object
blob对象表示 类似文件的原始数据 不可变 注意,不一定是js的原生数据类型
一般用于监听事件,可叫做监听器!
onload是在send请求发出且获得response会被调用
//---------页面加载成功(READYSTATE4)url设置回调函数----------//
req.onload = function () { //设定一个回调函数
if (req.status === 200) {
img.src = url; //设置img标签的src为指定url
}
else {
console.log('failed');
}
}
//----------页面加载失败的回调函数--------------------------------//
req.onerror = function () {
console.log("network error");
}
⑴定义一个回调函数;
⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
百科解释:
回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。
回调函数的使用可以大大提升编程的效率,这使得它在现代编程中被非常多地使用。同时,有一些需求必须要使用回调函数来实现。
最著名的回调函数调用有C/C++标准库stdlib.h/cstdlib中的快速排序函数qsort和二分查找函数bsearch中都会要求的一个与strcmp类似的参数,用于设置数据的比较方法。
记住嗷!open并不会发送请求,发送请求的是send函数
req.send();
为我们提供了操作http的机会,使我们有能力完成本应由浏览器完成的发送和接受http
数据的能力。
const url = "https://c-ssl.duitang.com/uploads/item/202002/24/20200224112148_nknod.jpg" //要渲染的图片链接
const img = document.getElementById('img');
req.open('GET', url); //以GET方式请求url资源
req.responseType = 'blob';
req.onload = function () { //设定一个回调函数
if (req.status === 200) {
img.src = url; //设置img标签的src为指定url
}
else {
console.log('failed');
}
}
req.onerror = function () {
console.log("network error");
}
req.send();
No Promise 和 Promise的区别,图片直观感知。
但是,设想这样一个情景,我们想要两个图片依次加载,我们命名了一个新的XHR对象req_new,现在我们有两个xhr对象,第一个加载req,第二个加载req_new,为了实现依次加载这个情景,我们需要在req的onload函数里面调用req_new的send方法,会显得代码十分糟糕!!!代码如下:
const req = new XMLHttpRequest(); //创建XMLHttpRequest对象
const url = "https://c-ssl.duitang.com/uploads/item/202002/24/20200224112148_nknod.jpg" //要渲染的图片链接
const req_new = new XMLHttpRequest();
const img = document.getElementById('img');
const img_new = document.getElementById('img_new');
//初始化req,onload里面调用req_new的send
req.open('GET', url); //以GET方式请求url资源
req.responseType = 'blob';
req.onload = function () { //设定一个回调函数
if (req.status === 200) {
img.src = url; //设置img标签的src为指定url
//调用req_new的send
req_new.send();
}
else {
console.log('failed');
}
}
req.onerror = function () {
console.log("network error");
}
//send req同时进入了req的onload并调用req_new的send
req.send();
/**
* 以下这一段代码,仅负责初始化req_new,若要唤起onload,调用req_new.send()后才可以
*/
req_new.open('GET', url); //以GET方式请求url资源
req_new.responseType = 'blob';
req_new.onload = function () { //设定一个回调函数
if (req_new.status === 200) {
img_new.src = url; //设置img标签的src为指定url
}
else {
console.log('failed');
}
}
req_new.onerror = function () {
console.log("network error");
}
由于onload的逻辑,导致上述代码十分难看,如果有多个xhr会令人很绝望,因此我们引入了promise来解决这一问题!
(ES6 标准下新增的函数)
相当于匿名函数,甚至{}和return都可以省略
x => x * x
相当于
function (x) {
return x * x;
}
定义如下:
[外部变量访问方式说明符] (参数表) -> 返回值类型
{
语句块
}
外部变量访问方式说明符:
可以是=或&,表示{}中用到的、定义在{}外面的变量在{}中是否允许被改变。=表示不允许,&表示允许。当然,在{}中也可以不使用定义在外面的变量。“-> 返回值类型”可以省略
怎么去理解这一段话呢?
可以理解为,给{ }语句块所引用的内容加上了static,不允许改变值,只能用值
代码示例:
int a[4] = {11, 2, 33, 4};
sort(a, a+4, [=](int x, int y) -> bool { return x%10 < y%10; } );
for_each(a, a+4, [=](int x) { cout << x << " ";} );
这段程的输出结果是:
11 2 33 4
程序第 2 行使得数组 a 按个位数从小到大排序。具体的原理是:sort 在执行过程中,需要判断两个元素 x、y 的大小时,会以 x、y 作为参数,调用 Lambda 表达式所代表的函数,并根据返回值来判断 x、y 的大小。这样,就不用专门编写一个函数对象类了。
第 3 行,for_each 的第 3 个参数是一个 Lambda 表达式。for_each 执行过程中会依次以每个元素作为参数调用它,因此每个元素都被输出。
好了,我们大概了解了一下,匿名函数,接下来我们继续看promise吧!!!
我们一定要清楚,js语言运行的环境时单线程的,也就是说一次只能完成一个任务,也就是一条流水线,如果有多个任务就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。这与java的多线程环境截然不同,所以要加以区分,之前在b站看视频,有印象有视频提到解决这个问题的方法,好像就是靠异步来解决。
对于异步的理解,可以改变程序执行顺序的操作就可以看成为异步操作
而解决方案有两种,最基础的异步是setTimeout和setInterval函数如一下代码所示
console.log("小明");
setTimeout(function(){
console.log("小张");
},500);
setTimeout(function(){
console.log("小刘");
},500);
setTimeout(function(){
console.log("小王");
},500);
console.log("小高")
输出顺序是,小明,小高,小张,小刘,小王
因此,小明和小高所在的流水线同步了,而setTimeout函数进入了任务队列
任务队列是事件队列,IO设备完成一项任务,就在任务事件中添加一个事件(许可),表示相关异步任务可以进入主线程了,主线程读取任务队列,任务队列除了IO设备事件,还有用户鼠标点击,页面滚动等一系列事件。
异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行相应的回调函数。例如ajax的success,complete,error也都指定了各自的回调函数,这些函数就会加入任务队列中,等待执行。
第二种实现异步也就是下面要说的promise,它可以避免层层回调函数嵌套,导致代码很糟糕。
具体是这么说的:
利用promise可以_将异步操作以同步操作的流程表达出来_,避免了层层嵌套的回调函数。此外,promise对象提供统一的接口,使得控制异步操作更加容易。
但注意promise无法取消,一旦建立就会立即执行,无法中途取消。而且,如果不设置回调函数,promise内部抛出的错误不会反映到外部。当处于Pending状态时,无法得知进展到哪一个阶段
ps:啥是pending状态??
百度了一下,pending状态其实就是未决的意思,悬而未决,例如请求已经发送,但是还没有得到回返。
promise的三种状态:
Pending-promise的初始状态,等到任务完成或是被拒绝;Resolved-执行完成并且成功的状态;Rejected-执行完成并且失败的状态。此三个状态不能相互逆转
then方法:
promise对象必须实现then方法,then是promise的核心!then方法必须返回一个promise对象,在promise对象里,可以注册多个then方法,并且回调的执行顺序跟注册顺序一致哦
ps:then方法接收两个回调函数,他们分别是成功时的回调和失败时的回调
var promise = new Promise(function(resolve, reject){
if(/*异步任务执行成功*/){
resolve(value);
}else{
reject(error);
}
});
value代表异步任务执行成功时从promise函数获取的值,可以理解为函数的私有变量,将promise值拿来,在then中应用
现在,我们修改一下最开始我们写的异步代码
function p(img) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest(); //创建XMLHttpRequest对象
req.open('GET', url);
req.responseType = 'blob';
req.onload = function () {
if (req.status === 200) {
img.src = url;
return resolve(req.response); //响应成功
}
else {
console.log('failed');
return reject(Error('failed')); //响应失败
}
}
req.onerror = function () {
console.log("network error");
return reject(Error('Network error'));
}
req.send();
})
}
p(img).then((res) => {
p(img_new); //then的神奇之处,如果return p()的话,还可以再接一个then
console.log(res)
});
then的特点,无限回调,只要你在本次then中又返回了一个promise,你就可以在下一个then中使用本次返回的promise
//稍微改一下调用函数的逻辑
p(img).then((res) => {
console.log(res)
return p(img_new); //then的神奇之处,如果return p()的话,还可以再接一个then
}).then((res) => {
console.log(res);
});
p(img).then((res) => {
console.log(res)
return p(img_new); //then的神奇之处,如果return p()的话,还可以再接一个then
}).then((res) => {
console.log(res);
return p(img2)
}).then((res) => {
console.log(res);
return p(img3)
}).then((res) => {
console.log(res);
return p(img4)
});
p(img).then((res) => {
console.log(res)
return p(img_new); //then的神奇之处,如果return p()的话,还可以再接一个then
}).then((res) => {
console.log(res);
return p(img2)
}).then((res) => {
console.log(res);
return p(img3)
}).then((res) => {
console.log(res);
return p(img4)
}).catch((err)=>{ //捕捉过程中的全部异常
console.log(err);
});
参考:
1.CSDN-橡胶米东:《js-promise的用法》
2.Bilibili UP:大翔_Arcxiang:《异步Javascript - 从XMLHttpRequest到Promise》
3.CSDN yangxiaodong88 《=> js 中箭头函数使用总结》
4.jiangzhenwei6 《window.XMLHttpRequest详解》