Sharing2 -- 如何在项目中写好你的ajax
ajax我估计自称前端的工程师很少有人会承认“我不会写”, jquery为我们提供了很好的接口,更降低了ajax的难度,但是在项目中,可能用到对ajax的统一管理,所以,我们需要学习在项目中如何写好我们的ajax。
1.为什么要在项目中为ajax写统一的接口?
我们在做项目的后期,有两个才提出的需求,如果没有统一的接口,修改起来将会很艰难。这件事让我觉得的在项目中有统一的接口很有必要,直接看例子吧。
我们的接口形式:
// AJAX PROMISE LAND
sunny.get = function(options){
… …
return $.ajax({
cache: options.cache,
url: options.url,
dataType: options.dataType,
contentType: options.contentType,
method: options.type,
data: options.data
})
.done(function(response){
if (typeof options.done == 'function') { options.done(response); };
})
.fail(function(error){
if (typeof options.fail == 'function') { options.fail(error); };
})
.always(function(response){
if (typeof options.always == 'function') { options.always(response); };
});
}
需求1: 请求期间要有loading…
//在我们的项目ajax接口上加loading。。。
// AJAX PROMISE LAND
sunny.get = function(options, className){
//在这个地方加上loading。。。
sunny.addLoader(className);
… …
return $.ajax({
… …
})
.done(function(response){
if (typeof options.done == 'function') { options.done(response); };
})
.fail(function(error){
if (typeof options.fail == 'function') { options.fail(error); };
})
.always(function(response){
//在这个地方去掉loading。。。
dcc.removeLoader(className);
if (typeof options.always == 'function') { options.always(response); };
});
}
这个时候有人会说,我不用统一接口,我也不用每个ajax挨个去加啊,那太傻了。我可以用$.ajaxSetup来做啊。所以我们需求2恰好就用了这个$.ajaxSetup, 但是我要说这是个灾难,不要这么写,除非你也逼不得已。
//需求2: session Timeout 要求跳转页面
//我们在这个平台早期没有做统一的接口,所以我们逼不得已
$.ajaxSetup({
beforeSend: function(XMLHttpRequest){
… …
},
complete: function(XMLHttpRequest,textStatus){
var sessionstatus = XMLHttpRequest.getResponseHeader("sessionTimeout");
var url = XMLHttpRequest.getResponseHeader("redirectUrl");
if(sessionstatus == "sessionTimeout"){
window.location.href = url;
}
}
});
但是: 这是一种可能被覆写的方式,你只能找没有用过的api,而且不能用deferred对象,找不到就没有办法了.
//举个栗子:
$.ajaxSetup({
success: function(res){
alert("success");
},
complete:function(XMLHttpRequest,textStatus){
alert("complete");
}
});
$.ajax({
url: './symbol/symbol.html',
dataType: 'html',
success: function(res){
alert(res);
},
error: function(error){
console.log(error);
}
});
//你可以去试试这个结果是什么,它只会弹yes,complete 而不会弹success,所以这就是我说的这是一种可能被覆写的方式,你根本不能确定会全局把控。
2.其实我有更好的写法,但是之前,你要看一看ES6的promise
2-1.我们不用promise,就用ajax有啥问题?
//主要有两个问题:
$.ajax({
url: '......',
success: function (data) {
$.ajax({
// 要在第一个请求成功后才可以执行下一步
url: '......',
success: function (data) {
// ......
}
});
}
});
缺点1: 在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的 Callback Hell。
缺点2: 如果几个异步操作之间并没有前后顺序之分(例如不需要前一个请求的结果作为后一个请求的参数)时,同样需要等待上一个操作完成再实行下一个操作。
所以,Promise对象可以用链式结构组织代码,并且可以同时执行多个操作,它就是为解决这两个问题而生的。那么它是如何解决的。
//针对缺点1, 用then来解决:
function test (ready) {
return new Promise(function (resolve, reject) {
if (ready) {
resolve("success!");
//将 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作
} else {
reject("error!");
//将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作
}
});
}
test(true).then(function (message) {
alert(message);
}, function (error) {
alert(error);
});
//Promise对象有三种状态,成功()/失败(onRejected)/初始态onFulfilled
//Promise对象重要的方法
//then 用于链式调用
//then每一次执行总会返回一个 Promise 对象,
//并且在 onFulfilled 时的返回值,可以作为后续操作的参数
test(true).then(function (message) {
return message;
}).then(function (message) {
return message + ',again';
}).then(function (message) {
return message + ',and again';
}).then(function (message) {
alert(message);
});
//catch 用来捕获错误的简写方法
then(fn).catch(fn),相当于 then(fn).then(null, fn)
test(false).catch(function(message){
alert(message);
});
//针对缺点2, 用all来解决:
//Promise.all
//可以接收一个元素为 Promise 对象的数组作为参数,
//当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回
function test2 () {
return new Promise(function (resolve) {
setTimeout(function(){
resolve("test2 success!");
}, 5000);
});
}
Promise.all([test(true), test2()]).then(function (result) {
console.log(result);
});
//Promise.race
//只要该数组中的 Promise 对象的状态发生变化该方法都会返回
Promise.race([test(true), test2()]).then(function (result) {
console.log(result);
});
2-2,Promise对象的兼容性问题,IE11及以下不支持,其他主流浏览器最新版本都支持了。但是如果你想支持全浏览器,怎么办?
如果要兼容旧的浏览器,建议可以寻找一些第三方的解决方案,例如 jQuery 的 $.Deferred。所以我们还要说说Deferred
3.看来我们还要看一看jQuery 的 $.Deferred了
$.Deferred是用来解决promise兼容问题的,所以我们重点看的也是通过$.Deferred如何解决那两个缺点。
//针对缺点1, 用done/fail/always来解决:
// 高于1.5.0版本的jquery,ajax返回的是deferred对象,可以进行链式操作
$.ajax({
url: './fake/test.json',
type: 'POST',
dataType: 'json',
data: {param1: 'value1'},
})
.done(function(data) {
console.log(data);
data.test = "ssss";
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
})
.done(function(data) {
//这个data中包含了data.test
console.log(data);
});
//针对缺点2, 用$.when()来解决:
$.when(
$.ajax({
url: './fake/test.json',
type: 'POST',
dataType: 'json',
data: {param1: 'value1'},
}),
$.ajax({
url: './fake/test2.json',
type: 'POST',
dataType: 'json',
data: {param1: 'value1'},
})
).done(function(data,data2) {
console.log("=============");
console.log(data); //test.json
console.log(data2); //test1.json
});
4.如果你看懂了我上面的胡言乱语,恭喜你,下面讲新的方法,估计你很快能懂。
function getAjax(option) {
return $.when(
$.ajax({
url: option.url,
method: option.method,
data: option.data,
dataType: 'json',
})
//扩展
).then(function(message){
return option.done(message);
},function(error){
option.fail();
}).then(function(message){
option.done1(message);
},function(error){
});//扩展
}
getAjax({
url: './fake/test.json',
method: 'POST',
data: { param1: 'value1' },
done: function(message){
return message.name + ' cool';
},
fail: function(){
alert('error');
},
done1: function(message){
console.log(message + ' !');
},
});
牛逼再吹一吹,这个ajax接口,可以链式,可以并行调用,还保留了原有接口统一管理的优点。