1. 为什么需要Promise
先看一个需求
通过ajax请求id, 再根据id请求用户名, 再根据用户名获取email
$.ajax({
// 请求id
type: 'GET',
url: './data1.json',
success: function (res) {
const { id } = res
// 根据id获取用户名
$.ajax({
type: 'GET',
url: 'data2.json',
data: { id },
success: function (res) {
const { username } = res
// 根据username获取email
$.ajax({
type: 'GET',
url: 'data3.json',
success: function (res) {
console.log(res);
// 更多回调嵌套...
}
})
}
})
}
})
回调地狱
以上这种在回调函数中嵌套回调的情况, 我们称之为回调地狱
Promise解决了回调地狱
2. Promise的基本使用
Promise
是一个构造函数, 通过new关键字实例化对象
语法
new Promise((resolve, reject) => {})
- Promis接受一个函数作为参数
- 在参数函数中接受两个参数 (也是函数)
- resolve
- reject
Promise实例
Promise实例有两个属性
- state: 状态
- result: 结果
1) promise状态
第一种状态: pending (准备 / 待解决 / 进行中)
第二种状态: fulfiled (已完成 / 成功)
第三种状态: rejected (已拒绝 / 失败)
2) promise状态的改变
通过调用resolve()
和reject()
改变当前promise对象的状态
示例
const p = new Promise((resolve, reject) => {
// resolve(): 调用函数, 使当前promise对象的状态改成 fulfilled
resolve()
})
console.dir(p);
示例
const p = new Promise((resolve, reject) => {
// reject(): 调用函数, 使 当前promise对象的状态改成 rejected
reject()
})
console.dir(p);
- resolve(): 调用函数, 使当前promise对象的状态改成 fulfilled
- reject(): 调用函数, 使 当前promise对象的状态改成 rejected
promise状态的改变是一次性的
3) promise的结果
const p = new Promise((resolve, reject) => {
// 通过给resolve() / reject() 传递参数, 改变 当前promise对象 的结果
resolve('成功的结果')
// reject('失败的结果')
})
console.dir(p);
3. Promise的方法
查看Promise原型上的方法
所有这些方法都可以应用于 Promise 对象的一个实例,并且所有这些方法依次返回一个新的Promise对象
1) then() 方法
作用: 拿到promise的结果并根据结果进行下一步操作
then()方法的参数:
- 第一个参数是一个函数
- 第二个参数也是一个函数
then方法的第一个参数是 resolved 状态的回调函数,第二个参数(可选)是 rejected 状态的回调函数
但是为了代码的可读性,我认为最好将它们分开 => 在catch中进行失败处理
then()方法的返回值: 是一个promise对象
示例
const p = new Promise((resolve, reject) => {
resolve()
})
p.then(() => {
// 当promise的状态时fulfilled时, 执行该函数
console.log('成功时调用');
}, () => {
// 当promise的状态时rejected时, 执行该函数
console.log('失败时调用');
})
then()方法获取数据
示例
const p = new Promise((resolve, reject) => {
// 通过给resolve() / reject() 传递参数, 改变 当前promise对象 的结果
resolve('成功的结果')
// reject('失败的结果')
})
p.then((res) => {
// 当promise的状态时fulfilled时, 执行该函数
// 这里的参数res是调用resolve时传递的参数 err同理
console.log('成功时调用', res);
}, (err) => {
// 当promise的状态时rejected时, 执行该函数
console.log('失败时调用', err);
})
then()方法的返回值 => 是一个promise对象, 状态是 pending
因此可以采用链式写法,即then方法后面再调用另一个then方法。
示例
new Promise((resolve,reject) => {}).then().then().then()...
2) catch() 方法
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。
另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
示例
const p = new Promise((resolve, reject) => { // 通过给resolve() / reject() 传递参数, 改变 当前promise对象 的结果 reject('失败的结果') }) p.then((res) => { // 当promise的状态时fulfilled时, 执行该函数 console.log('成功时调用', res); }) p.catch((err) => { // 当状态为rejected时, 执行该函数 console.log('失败了', err); }) // 失败了 失败的结果
比较推荐的书写方法是先使用then来给promise添加resolve的回调,然后使用catch来捕获所有的链接上的reject或者异常。
3) 关于链式操作中promise状态的改变
首先要知道: promise的状态不改变, 不会执行then / catch里面的方法
// 如果promise的状态时pending,不会执行then/ catch中的方法new Promise((resolve, reject) => {}).then(() => { console.log('成功');}).catch((err) => { console.log('失败');})
在then中, 通过 return 可以将返回的新的promise实例改为fulfilled状态
new Promise((resolve, reject) => { resolve('success data')}) .then((res) => { return res}) .then((res) => { console.log(res);})// success data
在then 中, 通过 抛出异常 可以将返回的新的promise实例改为rejected状态
new Promise((resolve, reject) => { resolve('success data')}) .then((res) => { console.log('成功'); // 这行代码会报错, 会将返回的promise对象的状态改成rejected console.log(a);}) .then((res) => { console.log('成功');}).catch((err) => { console.log(err);})