Promise详解(三)— Promise封装Ajax | 青训营笔记

223 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第3天

(三)Promise封装Ajax

🟢先看看单独的Ajax

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
          //do something
       }
   }
}
xhr.open('GET',"https://api.apiopen.top/GetJoke");
xhr.responseType = 'json';
xhr.send();

如果修改了Ajax中open的地址为错误的地址后,xhr的readyState和status都会变化吗?

  1. xhr.readyState 依旧等于 4,只不过之前返回的是get到的资源,而现在返回的是没有get的错误信息。(无论地址对不对服务器都会有反应)

  2. 如果将https://api.apiopen.top/GetJoke改成https://api.apiopen.top/GetJoke2 那status会变成404。联系上服务器了,但是服务器没有所需要的资源。

    如果将https://api.apiopen.top/GetJoke 改成https://api2.apiopen.top/GetJoke 那status会 变成0。根本没有联系上服务器

所以,我们不能仅仅通过readyState==4就判断已经加载完毕,因为这有可能是成功的资源加载完毕,也有可能是错误的信息加载完毕。


从上面我们可以得到何时调用resolve函数,何时调用reject函数👇🏻

📌数据请求成功 ⇒ xhr.readyState = 4 && xhr.status = 200 ⇒ 切换成fulfilled状态 ⇒ 调用resolve函数,传值为xhr.reponse。

📌数据请求失败 ⇒ xhr.readyState = 4 但是xhr.status != 200 ⇒

切换为rejected状态 ⇒ 调用reject函数,传值为“请求出错”

🟢promise配合Ajax使用

var p = new Promise((resolve,rejected)=>{
  //开启一个异步任务
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
	    if(xhr.readyState == 4){
	        if(xhr.status == 200){
	          resolve(xhr.response);
	       }else{
            reject("请求失败");
        }
	   }
	}
	xhr.open('GET',"https://api.apiopen.top/GetJoke");
	xhr.responseType = 'json';
	xhr.send();
})

p.then(
   (value)=>{console.log('成功了',value);},
   (reason)=>{console.log('失败了',reason);}
)

🟢封装Ajax为一个函数

上面Promise配合Ajax的示例中,请求的地址是写死的,参数也没带,封装一个通用性更强的函数(可以传入请求地址和参数的),名为sendAjax

📌要求:对get请求进行封装。接收两个参数,url(请求地址)和data(请求参数)。返回值为一个Promise示例。

⇒ 我传入的请求参数是参数对象,而url后跟的请求参数的格式是query。所以要转化一下。

即:{page:1,count:2,type:video}转化为page=1&count=2&type=video

 function  sendAjax (url, data){
	 var p = new Promise((resolve,reject)=>{
		  //示例xhr
		   const xhr = new XMLHttpRequest();
		  //绑定监听
		   	xhr.onreadystatechange = function(){
			    if(xhr.readyState == 4){
			        if(xhr.status >= 200 && xhr.status < 300){
			          resolve(xhr.response);
			       }else{
		            reject("请求失败");
		        }
			   }
			}
		
		  //参数对象的转化
			var str = '';
			  for(let key in data){
			    str+='${key}=${data[key]}&'
			}
		  str = str.slice(0,-1);  //去掉最后一个&
			xhr.open('GET',url + '?'+ str);
			xhr.responseType = 'json';
			xhr.send();z
		 })
	 return p;
}

在下方调用函数:

const p =sendAjax("https://api.apiopen.top/GetJoke",{page:1,count:2,type:video});
console.log(p);

输出结果为:Promise<pending>,为什么不是Promise<fulfilled>呢?明明已经请求成功了啊

原因:请求成功后改变状态的回调函数是异步的,必须等到主线程执行完毕后才能执行,所以只有当console.log(p);执行完后才会修改promise实例对象的状态。

实际上,输出promise实例是不规范的,应该用.then方法指定成功和失败的回调。

应该修改为:

const p =sendAjax("https://api.apiopen.top/GetJoke",{page:1,count:2,type:video});
p.then(
   (value)=>{console.log('成功了',value);},
   (reason)=>{console.log('失败了',reason);}
);