你可能见到过这样的代码:
$.ajax({
url:"xServlet"
}).done(function (xret){
console.log(xret);
$.ajax({
url:"y1Servlet",
data:xret,
success:function(y1ret){
console.log(y1ret);
}
});
$.ajax({
url:"y2Servlet",
data:xret,
success:function(y2ret){
console.log(y2ret);
}
});
}).fail(function(xret){
console.log(xret);
});
执行结果:
意思是,请求地址y1Servlet和y2Servlet依赖请求地址xServlet的执行,这时候我们只能使用这种嵌套的方式才能确保正确的ajax执行顺序,上面的代码我仅仅只是为了演示,真实的情况可能远比上面的代码复杂,因为真实的情况可能还要处理很多的东西,嵌套的层数也可能会更多,对于这种不便于阅读、不便于维护的嵌套,大家都“亲切地”称之为“嵌套地狱”,可想而知,它的可怕性。
es6中为了解决这种嵌套的问题,推出了Promise这个类,怎么使用呢?那我就先使用Promise把上面的类改写一番,大家比较一下可阅读性:
<script>
function ajaxRequest(reqesturl,requestdata){
let dtd=$.Deferred();
$.ajax({
url: reqesturl,
data: requestdata
}).done(function(result){
dtd.resolve(result);
}).fail(function(err){
dtd.reject();
});
return dtd.promise();
}
var px=new Promise(function(resolve,reject){
ajaxRequest("xServlet").done(function (xret){
resolve(xret);
}).fail(function(xret,textStatus){
reject(xret);
console.log(textStatus);
});
});
//Promise对象(这里一px为例)的then有两个参数,then(f1,f2),当px的状态为成功(执行了resolve(xx))的时候进入
//f1,当px的状态为成功(执行了reject(xx))的时候进入f2
var py1=px.then(function(successMsg){
console.log("px这个Promise对象状态为成功的时候进入");
console.log(successMsg);
return new Promise(function(resolve,reject){
ajaxRequest("y1Servlet",successMsg).done(function (y1ret){
resolve(y1ret);
});
});
},function(errorMsg){
console.log("px这个Promise对象状态为失败的时候进入");
});
var py2=px.then(function(successMsg){
return new Promise(function(resolve,reject){
ajaxRequest("y2Servlet",successMsg).done(function (y2ret){
resolve(y2ret);
});
});
}).catch(function (errorMsg){//相当于then的第二个参数
console.log("px这个Promise对象状态为失败的时候进入");
console.log(errorMsg);
});
Promise.all([px,py1,py2]).then(function(arr){
console.log("px,py1,py2这3个Promise对象状态都为成功的时候进入");
console.log(arr)
});
</script>
先贴上执行结果:
你看了肯定会吃惊,我擦,几行代码让你改写一下翻了一倍了,这么麻烦,我用它干什么?我为了尽可能的把Promise对象讲解清楚,把它的方法都用上,写了很多冗余的代码,才导致你看起来代码很多,其实代码的多少不耽误你的阅读速率,关键在于是否具有很强的可读性。
你看到上面的代码,可能就像是看天书,完全不知道哪跟哪,那么该如何理解上面的代码呢?
从上往下看:
1.先看ajaxRequest这个函数,这里用到了jQuery的deferred对象,可以参考“jquery里面的deferred对象”这篇教程。
2.创建一个Promise对象的格式:var promise1=new Promise(function(resolve,reject){});其中参数resolve和reject本质是一个函数,因此你才可以看到下面的函数调用方式-------resolve(xret)以及reject(xret), 那么调用resolve函数和reject函数有什么用呢?
3.调用resolve函数和reject函数的作用分别是修改对应的Promise对象(这里假设对象名字叫做promise1)promise1的状态为Fulfilled和Rejected,Promise的实例对象有三种状态:
Fulfilled :成功
Rejected :失败
pending:刚初始化
也就是说,resolve(xret)会修改promise1的状态为成功,reject(xret)会修改promise1的状态为失败。
4.程序继续往下走,到了promise1.then这个方法,then有两个参数,then(f1,f2),当px的状态为成功(执行了resolve(xx))的时候进入f1,当px的状态为成功(执行了reject(xx))的时候进入f2。f1和f2的参数就是执行resolve(xret)或reject(xret)时传入的参数xret。then 可以使用链式调用,每一次执行then时总是会返回一个 Promise 对象。另外,在 then onFulfilled 的函数当中的返回值,可以作为后续操作的参数。
f1().then(function (msg) {
return msg;
}).then(function (msg) {
return msg + ' 1';
}).then(function (msg) {
return msg + '2';
}).then(function (msg) {
alert(msg);
});
5.catch的作用和then的第二个参数f2的作用一样,都是当promise1的状态为失败的时候去执行。catch
方法是 then(onFulfilled, onRejected)
方法当中 onRejected
函数的一个简单的写法,也就是说可以写成 then(fn).catch(fn)
,相当于 then(fn).then(null, fn)
。
6.Promise.all(Promise实例的数组a).then(function(arr){})的作用是当a里面的所有Promise实例的状态都为成功的时候才去执行后边的then,这里then的参数arr就是数组a,你可以在这里编写自己的逻辑。
另外,还有个Promise.race,它同样接收一个数组,跟Promise.all的格式是完全一样的,不同的是只要该数组中的 Promise 对象的状态有一个发生变化(无论是 resolve(即成功) 还是 reject(即失败))该race方法都会执行后边的then。
附录:
xServlet:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
List<String> xList=new ArrayList<String>();
xList.add("2018-03-20");
xList.add("2018-03-21");
xList.add("2018-03-22");
JSONArray jsonArray=JSONArray.fromObject(xList);
out.println(jsonArray);
}
y1Servlet:
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
Map<String, Object> retMap=new HashMap<String, Object>();
retMap.put("2018-03-20", "33");
retMap.put("2018-03-21", "44");
retMap.put("2018-03-22", "55");
JSONObject jsonObject=JSONObject.fromObject(retMap);
out.println(jsonObject);
y2Servlet:
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
Map<String, Object> retMap=new HashMap<String, Object>();
retMap.put("2018-03-20", "赵云");
retMap.put("2018-03-21", "阿珂");
retMap.put("2018-03-22", "武则天");
JSONObject jsonObject=JSONObject.fromObject(retMap);
out.println(jsonObject);