使用 ES6 Promise 对 jQuery.ajax 方法进行简易封装
日常开发业务中我们经常会遇到几个接口需要同步调用的情况,举个例子:
- 页面发起请求到 接口1 获取本机的 外网ip
- 获取到 外网ip 后,又需要请求 接口2 查询 运营商、归属地 等信息
- 同时页面有另一个无关上面两个的数据需求,需要请求 接口3
项目中使用 $.ajax
去请求接口,最简单粗暴的方式其实就是把 {"async": true}
这个默认值改为 false
:
$.ajax({
url: 'http://domain-requested:port/api/endpoint',
type: 'GET',
data: {},
dataType: 'json',
async: false,
success: function (data, status) {
//前端需要对data做的操作写在这个回调里
},
error: function (XMLHttpRequest, textStatus, errThrown) {
//请求失败的情况处理写在这个回调里
}
});
但是这样一来,所有的接口都是同步请求的,如果这个页面有非常多的请求需要执行,那么响应速度不是就很慢吗?于是我想到了 ES6 Promise 的写法。
非专业前端开发,编码技术拙劣,仅做记录供以后参考。请多指点交流
代码如下:
/**
* 通用请求方法($.ajax, Promise)
* @param url 请求地址
* @param options 请求参数
* @return ECMAScript 6 Promise对象
*/
function request(url, options={}) {
//Promise相关知识还请自行查阅,这里只给个大致思路
return new Promise((resolve, reject) => {
try {
//从cookie里拿令牌给服务端鉴权
//这个getCookie不是内置的方法,但网上有很多类似的
const token = getCookie('token');
const reqOptions = {
url: '',
type: 'GET',
data: {},
dataType: 'json',
async: true,
contentType: 'application/x-www-form-urlencoded',
headers: {
Authorization: `Bearer ${token}`,
},
complete: function (XMLHttpRequest, textStatus) {
//complete回调是只要请求结束就执行
//一般业务中这里可能会需要关闭页面上的loading遮罩之类的操作
if (!!options.complete && typeof options.complete === "function") {
options.complete(XMLHttpRequest, textStatus);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
//error回调是请求发生错误时会进来
//需要注意服务端返回形如{"code": 500}的报文,但HTTP响应码依然为200的这种不会进来的
console.log(`'${url}' request failed: ${textStatus}`);
if (!!options.error && typeof options.error === "function") {
options.error(XMLHttpRequest, textStatus, errorThrown);
}
//reject出去的对象,在promise.catch里可以拿到
reject({
'url': url,
'xhr': XMLHttpRequest,
'status': textStatus,
'error': errorThrown,
});
},
success: function (data, textStatus, jqXHR) {
//success回调时请求成功时执行
if (!!options.success && typeof options.success === "function") {
options.success(data, textStatus, jqXHR);
}
//resolve出去的对象,在promise.then里可以拿到
resolve({
'url': url,
'data': data,
'status': textStatus,
'xhr': jqXHR,
});
},
};
if (!token || token === 'undefined' || token === 'null') {
delete reqOptions.headers.Authorization;
}
//用自定义options扩展默认options,去调用jQuery.ajax
$.ajax($.extend({}, reqOptions, $.extend({}, options, {'url': url})));
} catch (error) {
//做个异常处理,把错误信息reject出去
reject({
'url': url,
'error': error,
});
}
});
}
调用案例:
$(document).ready(function () {
// 模拟业务:先获取ip再获取运营商信息 对接口2来说ip是必要参数
getIpAddrLocation().then(result => {
console.log(`获取到当前ip为: ${result.data.ip}`);
getISP(result.data.ip).then(res => {
console.log(`获取到ip:'${result.data.ip}'的运营商信息: '${JSON.stringify(res.data)}'`);
}).catch(err => {
console.log(`获取到ip:'${result.data.ip}'的运营商信息时错误: '${err.error}'`);
});
}).catch(err => {
console.log(`获取到当前ip时出错: ${err.error}`);
});
// 模拟:和ip不相关的业务 不管上面两个完成没有都异步获取
getLocalResp().then(result => {
console.log(`异步获取和ip无关的json报文:${JSON.stringify(result.data)}`);
}).catch(err => {
console.log(`异步获取和ip无关的json报文时出错: ${err.error}`);
});
});
/**
* 模拟获取本地响应,和其他业务无关的接口调用
* @return Promise
*/
async function getLocalResp() {
const url = 'http://localhost/frontend/mock/json_wrap.json';
return await request(url);
}
/**
* 模拟获取ip和一些其他的基本信息
* @return Promise
*/
async function getIpAddrLocation() {
//这里接口用nginx做了转发,不然localhost走外部接口会报跨域问题
const url = 'http://localhost/whois-pconline/ipJson.jsp';
return await request(url, {
data: {
json: true,
}
});
}
/**
* 模拟通过ip获取运营商等信息
* @return Promise
*/
async function getISP(ipaddr='') {
//这里接口用nginx做了转发,不然localhost走外部接口会报跨域问题
//const url = 'http://localhost/ip-taobao/service/getIpInfo.php';
const url = 'http://localhost/whois-pconline/ipJson.jsp';
return await request(url, {
data: {
ip: ipaddr,
json: true,
}
});
}
执行结果如下:
接口请求顺序如下: