Promise是异步编程的一种解决方案,可以解决传统Ajax回调函数嵌套问题。
- 传统的Ajax异步调用在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的Callback Hell
- 为了解决上述的问题,Promise对象应运而生,在EMCAScript 2015当中已经成为标准,Promise也是ES6的新特性
- Promise是异步编程的一种解决方案
- 从语法上说,Promise是一个对象,从它可以获取异步操作的消息
2.Promise应用实例
2.1需求分析/图解
- 需求:演示promise异步请求使用
- 执行效果:


2.2代码实现
使用json文件模拟数据库表数据
monster.json
| { | |
| “id”: 1, | |
| “name”: “黑山老妖” | |
| } |
monster_detail_1.json
| { | |
| “id”: 1, | |
| “address”: “黑山洞”, | |
| “skill”: “翻江倒海”, | |
| “age”: 500, | |
| “gfid”: 2 | |
| } |
monster_gf_2.json
| { | |
| “name”: “狐狸精”, | |
| “age”: 100 | |
| } |
2.2.1jquery-ajax实现多次ajax请求
| <html lang=“en”> | |
| <head> | |
| <meta charset=“UTF-8”> | |
| <title>jquery-ajax多次请求</title> | |
| <!–引入jquery–> | |
| <script type=“text/javascript” src=“script/jquery-3.6.0.min.js”></script> | |
| <script type=“text/javascript”> | |
| //jquery发出ajax的方式 | |
| $.ajax({ | |
| url: “data/monster.json”, | |
| success(resultData) {//第一次ajax成功的回调函数 | |
| console.log(“第一次ajax请求 monster 基本信息=”, resultData); | |
| //发出第二次ajax请求 | |
| $.ajax({ | |
| //第二次ajax请求的url根据第一次的结果来改变 | |
| url: `data/monster_detail_${resultData.id}.json`, | |
| success(resultData) {//第二次ajax成功的回调函数 | |
| console.log(“第二次ajax请求 monster 详细信息=”, resultData); | |
| }, | |
| error(err) { | |
| console.log(“第二次ajax请求出现异常=”, err); | |
| } | |
| }) | |
| }, | |
| error(err) { | |
| console.log(“第一次ajax请求出现异常=”, err); | |
| } | |
| }) | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |
使用jquery-ajax的方式容易出现回调函数嵌套(Callback Hell),如果发出的是多次ajax请求,那么嵌套的层数将会变得非常多,代码不易读。这种写法冗余度高,代码可维护性低。
2.2.2promise对象实现多次ajax请求
首先创建一个Promise对象,在该对象内传入一个箭头函数,箭头函数内进行ajax请求。Promise对象的箭头函数传入了两个方法作为参数(命名随意),这里的resolve方法是ajax请求成功后调用的函数,reject方法是ajax请求失败后调用的函数。不同于jquery的回调嵌套,在ajax请求成功的success方法中会调用resolve(),并且把第一次ajax请求获得的数据传入该方法。
resolve方法会跳到Promise对象的then()方法,then方法中可以继续使用$.ajax()去进行第二次ajax请求。在then方法中,又创建了一个Promise对象,该对象和之前的Promise对象中的业务逻辑相似,不同的是可以通过上一次的resolve方法,拿到第一次ajax请求返回的数据,然后根据数据进行第二次的ajax请求…….在第二次的ajax请求中,又可以获取第二次ajax请求返回的数据,然后调用这次Promise对象的resolve(),将新的数据传给这次Promise对象的then()方法…..
即,第二次ajax请求的resolve方法又会跳到Promise对象的then()方法……..以此类推。
整个过程形成了一个链式的调用。
| <html lang=“en”> | |
| <head> | |
| <meta charset=“UTF-8”> | |
| <title>使用promise完成多次ajax请求</title> | |
| <script type=“text/javascript” src=“script/jquery-3.6.0.min.js”></script> | |
| <script type=“text/javascript”> | |
| //先请求到monster.json | |
| //1.创建Promise对象 | |
| //2.构造函数需要传入一个箭头函数 | |
| //3.(resolve, reject)参数列表,resolve:如果请求成功,调用resolve函数 | |
| // reject:如果请求失败,调用reject函数 | |
| //4.箭头函数体内仍然是通过jquery发出ajax | |
| let promise = new Promise((resolve, reject) => { | |
| //发出ajax请求 | |
| $.ajax({ | |
| url: “data/monster.json”, | |
| success(resultData) {//第一次ajax请求成功的回调函数 | |
| console.log(“Promise发出的第一次ajax请求,返回的monster基本信息=”, resultData); | |
| resolve(resultData); | |
| }, | |
| error(err) { | |
| //console.log(“Promise第一次异步请求出现异常=”, err); | |
| reject(err); | |
| } | |
| }) | |
| }); | |
| //这里我们可以继续编写第一次请求成功之后的业务 | |
| //仍然使用箭头函数 | |
| promise.then((resultData) => { | |
| //这里可以继续发出请求 | |
| return new Promise((resolve, reject) => { | |
| $.ajax({ | |
| url: `data/monster_detail_${resultData.id}.json`, | |
| success(resultData) {//第二次ajax请求成功的回调函数 | |
| console.log(“Promise发出的第二次ajax请求,返回的monster详细信息=”, resultData); | |
| //可以继续进行下一次的请求 | |
| resolve(resultData); | |
| }, | |
| error(err) { | |
| //console.log(“Promise第二次异步请求出现异常=”, err); | |
| reject(err); | |
| } | |
| }) | |
| }) | |
| }).then((resultData) => { | |
| console.log(“promise.then().then(),resultDate”, resultData); | |
| //这里可以继续发出第三次ajax请求…. | |
| return new Promise(((resolve, reject) => { | |
| $.ajax({ | |
| url: `data/monster_gf_${resultData.gfid}.json`, | |
| success(resultData) {//第二次ajax请求成功的回调函数 | |
| console.log(“Promise发出的第三次ajax请求,返回的monster gf信息=”, resultData); | |
| //可以继续进行下一次的请求 | |
| //resolve(resultDate); | |
| }, | |
| error(err) { | |
| //console.log(“Promise第三次次异步请求出现异常=”, err); | |
| reject(err); | |
| } | |
| }) | |
| })) | |
| }).catch((err) => {//这里可以对多次ajax请求的异常进行处理 | |
| console.log(“promise异步请求异常=”, err); | |
| }) | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |

2.2.3promise代码重排
虽然在2.2.2中使用promise实现了多次ajax请求,解决了jquery-ajax的嵌套回调问题,但是代码仍然显得臃肿。创建Promise对象和进行ajax请求的代码是重复的,因此可以进行封装,代码重排,增强可读性。
| <html lang=“en”> | |
| <head> | |
| <meta charset=“UTF-8”> | |
| <title>promise代码重排</title> | |
| <script type=“text/javascript” src=“script/jquery-3.6.0.min.js”></script> | |
| <script type=“text/javascript”> | |
| /** | |
| * 这里我们将重复的代码封装,编写为get方法 | |
| * @param url ajax请求的资源 | |
| * @param data ajax请求携带的数据 | |
| * @returns {Promise<unknown>} | |
| */ | |
| function get(url, data) { | |
| return new Promise(((resolve, reject) => { | |
| $.ajax({ | |
| url: url, | |
| data: data, | |
| success(resultData) { | |
| resolve(resultData); | |
| }, | |
| error(err) { | |
| reject(err); | |
| } | |
| }) | |
| })) | |
| } | |
| //需求:完成 | |
| //1.先获取monster.json | |
| //2.再获取monster_detail_1.json | |
| //3.再获取monster_gf_2.json | |
| get(“data/monster.json”).then((resultData) => { | |
| //第一次ajax请求成功后的处理代码 | |
| console.log(“第1次ajax请求返回的数据=”, resultData); | |
| return get(`data/monster_detail_${resultData.id}.json`); | |
| }).then((resultData) => { | |
| //第二次ajax请求成功后的处理代码 | |
| console.log(“第2次ajax请求返回的数据=”, resultData); | |
| return get(`data/monster_gf_${resultData.gfid}.json`); | |
| }).then((resultData) => { | |
| //第三次ajax请求成功后的处理代码 | |
| console.log(“第3次ajax请求返回的数据=”, resultData); | |
| //如果还有就继续… | |
| }).catch(err => { | |
| console.log(“请求出现异常=”, err) | |
| }) | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |

3.练习
分别使用Jquery-Ajax和Promise代码重排,完成如下功能,发出3次ajax请求,获取对应的数据,注意体会Promise发出多次Ajax请求的方便之处。
student_100.json
| { | |
| “id”: 100, | |
| “name”: “jack”, | |
| “class_id”: 10 | |
| } |
class_10.json
| { | |
| “id”: 10, | |
| “name”: “java工程师班级”, | |
| “student_num”: 30, | |
| “school_id”: 9 | |
| } |
school_9.json
| { | |
| “id”: 9, | |
| “name”: “清华大学”, | |
| “address”: “北京” | |
| } |
3.1jquery-ajax方式
| <html lang=“en”> | |
| <head> | |
| <meta charset=“UTF-8”> | |
| <title>jquery-ajax</title> | |
| <script type=“text/javascript” src=“../script/jquery-3.6.0.min.js”></script> | |
| <script type=“text/javascript”> | |
| //第一次ajax请求 | |
| $.ajax({ | |
| url: “data/student_100.json”, | |
| success(resultData) { | |
| console.log(“第一次ajax请求=”, resultData); | |
| //第二次ajax请求 | |
| $.ajax({ | |
| url: `data/class_${resultData.class_id}.json`, | |
| success(resultData) { | |
| console.log(“第二次ajax请求=”, resultData); | |
| //第三次ajax请求 | |
| $.ajax({ | |
| url: `data/school_${resultData.school_id}.json`, | |
| success(resultData) { | |
| console.log(“第三次ajax请求=”, resultData); | |
| }, | |
| error(err) { | |
| console.log(“第三次ajax请求出现异常=”, err); | |
| } | |
| }) | |
| }, | |
| error(err) { | |
| console.log(“第二次ajax请求出现异常=”, err); | |
| } | |
| }) | |
| }, | |
| error(err) { | |
| console.log(“第一次ajax请求出现异常=”, err); | |
| } | |
| }) | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |

3.2Promise代码重排
| <html lang=“en”> | |
| <head> | |
| <meta charset=“UTF-8”> | |
| <title>promise代码重排</title> | |
| <script type=“text/javascript” src=“../script/jquery-3.6.0.min.js”></script> | |
| <script type=“text/javascript”> | |
| //get方法也可以封装到js工具类,重复使用 | |
| function get(url, data) { | |
| return new Promise(((resolve, reject) => { | |
| $.ajax({ | |
| url: url, | |
| data: data, | |
| success(resultData) { | |
| resolve(resultData); | |
| }, | |
| error(err) { | |
| reject(err); | |
| } | |
| }) | |
| })) | |
| } | |
| //业务 | |
| get(“data/student_100.json”).then(resultData => { | |
| console.log(“第1次返回的数据=”, resultData); | |
| return get(`data/class_${resultData.class_id}.json`); | |
| }).then(resultData => { | |
| console.log(“第2次返回的数据=”, resultData); | |
| return get(`data/school_${resultData.school_id}.json`); | |
| }).then(resultData => { | |
| console.log(“第3次返回的数据=”, resultData); | |
| //如有需求可以继续请求… | |
| }).catch(err => { | |
| console.log(“promise异步请求异常=”, err) | |
| }) | |
| </script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> |

