一、Promise的基本使用
Promise传入回调函数 ,一传入就立即执行,传入的这个函数被称为executor,回调函数里面又有两个回调函数。一个成功的回调,一个失败的回调。
js
// resolve: 回调函数, 在成功时, 回调resolve函数
// reject: 回调函数, 在失败时, 回调reject函数
const p = new Promise((resolve, reject) => {
console.log('立即执行')
// resolve('成功的回调')
reject('失败的回调')
})
js
// then方法可以传入两个回调函数,用于接收Promise的执行结果
// 第一个会在Promise执行resolve函数时获取成功的回调,第二个会在Promise执行reject函数时获取失败的回调。
p.then(res => {
console.log(res)
}, err => {
console.log(err) // 输出'失败的回调'
})
// catch方法传入的回调函数, 会在Promise执行reject函数时, 被回调
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // 输出'失败的回调'
})
// 如果是这样,catch将会成为第二个Promise对象失败的回调。(因为then方法也有返回值,是一个promise对象)具体可看下面then方法的使用
p.then(res => {
console.log(res)
}, err => {
console.log(err) // 输出'失败的回调'
return Promise.reject('第二个promise失败回调')
}).catch(err => {
console.log(err) // 输出'第二个promise失败回调'
})
注意:当 Promise 对象什么都不传时,执行会报错,规定必须给 Promise 对象传入一个执行函数,否则将会报错。
js
var promise = new Promise() // 报错
二、Promise的使用场景
Promise 对象代表一个异步操作,将异步操作以同步的流程表达出来,为了解决异步处理造成的回调地狱问题而产生的。在真实的开发场景中,数据的请求一般是异步的,且时间是不定的,我们可以模拟这样的场景。
js
function fn1() {
setTimeout(() => {
console.log('我')
}, 300)
}
function fn2() {
setTimeout(() => {
console.log('喜欢')
}, 200)
}
function fn3() {
setTimeout(() => {
console.log('你')
}, 100)
}
// 为了得到正确的返回数据,我们必须这样做
setTimeout(function () { //第一层
fn1()
setTimeout(function () { //第二程
fn2()
setTimeout(function () { //第三层
fn3()
}, 100)
}, 200)
}, 300)
// 依次输出我喜欢你
代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。这样代码的阅读性不好,而且定时器的时间还要随着数据返回的时间改变而改变(可以把fn1的时间变为1000试试),才能保证数据的正确返回顺序。
为了解决上述问题,我们可以让接口返回一个promise对象
js
function fn1() {
return new Promise(resolve => {
setTimeout(() => {
resolve('我')
}, 1000)
})
}
function fn2() {
return new Promise(resolve => {
setTimeout(() => {
resolve('喜欢')
}, 200)
})
}
function fn3() {
return new Promise(resolve => {
setTimeout(() => {
resolve('你')
}, 100)
})
}
fn1().then(res => {
console.log(res)
return fn2()
}).then(res => {
console.log(res)
return fn3()
}).then(res => {
console.log(res)
})
// 1s后输出'我', 再过200ms后输出'喜欢',再过100ms后输出'你'
这样不仅解决了回调地狱的问题,也能保证即使fn1的时间即使更改了,数据的执行顺序也能正确返回(具体原因,可了解一下js的执行机制。)
三、Promise有三种状态:
已成功 (Fulfilled) : 执行成功。
已拒绝 (Rejected) : 执行失败。
待定 (Pending) : 可以理解为Promise对象实列创建时候的初始状态,既没成功,也没拒绝。
Promise 一旦状态改变,就不会再变 ,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。
js
new Promise((resolve, reject) => {
// pending状态: 待定/悬而未决的
console.log('立即执行') // 输出'立即执行'
resolve('成功的回调') // 处于fulfilled状态(已敲定/兑现状态)
// 前面的状态已经更改,这里不会执行了
reject('失败的回调') // 处于rejected状态(已拒绝状态)
console.log('这里依然可以继续执行') // 输出'这里依然可以继续执行'
}).then(res => {
console.log('====', res) // 输出'成功的回调'
}).catch(err => {
console.log('===', err) // 状态已经改变了,这里不会输出了
})
// 注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
四、Promise的resolve参数
resolve的参数,可以传入的值:
1、一个普通的对象 --> pending -> fulfilled
js
new Promise((resolve, reject) => {
resolve('aaa') // pending -> fulfilled
}).then(res => {
console.log(res) // 输出'aaa'
}).catch(err => {
console.log(err)
})
2、传入一个promise --> 如果传入的是一个promise,那么当前的Promise的状态会由传入的Promise来决定,相当于状态进行了移交。
js
const myPromise = new Promise((resolve, reject) => {
reject('我发生了失败的转移')
})
new Promise((resolve, reject) => {
resolve(myPromise) // pending -> rejected
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // 输出'我发生了失败的转移'
})
3、传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口),那么也会执行该then方法, 并且又该then方法决定后续状态,也相当于状态进行了移交。
js
new Promise((resolve, reject) => {
const obj = {
then: function(resolve, reject) {
resolve('对象的then方法,自动执行了')
}
}
resolve(obj) // 传入一个带有then方法的对象
}).then(res => {
console.log(res) // 输出'对象的then方法,自动执行了'
}).catch(err => {
console.log(err)
})
五、Promise对象的方法
在了解Promise对象的方法之前我们可以先使用Object.getOwnPropertyDescriptors()方法看看Promise的原型上有哪些属性和方法
js
// Object.getOwnPropertyDescriptors 是 JavaScript 中的一个方法,它返回指定对象所有自身属性的描述对象
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
// 输出
// {
// constructor: {value: [Function: Promise],writable: true,enumerable: false,configurable: true},
// then: { value: [Function: then],writable: true,enumerable: false,configurable: true},
// catch: {value: [Function: catch],writable: true,enumerable: false,configurable: true},
// finally: {value: [Function: finally],writable: true,enumerable: false,configurable: true},
// [Symbol(Symbol.toStringTag)]: {value: 'Promise',writable: false,enumerable: false,configurable: true}
// }
// 说明由Promise构造的对象可以继承原型上的then,catch,finally等方法
1、Promise对象上的then方法
1.1、同一个Promise可以被多次调用then方法
当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
js
const myPromise = new Promise((resolve, reject) => {
resolve('成功的回调')
})
myPromise.then(res => {
console.log('res1:', res) // 输出'成功的回调'
})
myPromise.then(res => {
console.log('res2:', res) // 输出'成功的回调'
})
myPromise.then(res => {
console.log('res3:', res) // 输出'成功的回调'
})
1.2、then方法本身也是有返回值的, 它的返回值是Promise
1.2.1、如果我们返回的是一个普通值(数值/字符串/普通对象/undefined),那么这个普通的值被作为一个新的Promise的resolve值
js
const myPromise = new Promise((resolve, reject) => {
resolve('成功的回调')
})
myPromise.then(res => {
console.log(res)
// return {'name': 'wbb'}
// return 1111
// return 'bbb'
return
}).then(res => {
console.log(res) // 输出'undefined'
})
1.2.2、如果我们返回的是一个Promise,会对状态进行移交。
js
myPromise.then(res => {
return new Promise((resolve, reject) => {
reject('失败了')
})
}).then(res => {
console.log(res)
}, err => {
console.log(err) // 输出'失败了'
})
1.2.3、如果返回的是一个对象, 并且该对象实现了thenable,会对状态进行移交。
js
myPromise.then(res => {
return {
then: function(resolve, reject) {
resolve(222222)
}
}
}).then(res => {
console.log("res:", res) // 输出222222
})
2、Promise对象上的catch方法
2.1、通过catch方法来传入错误(拒绝)捕获的回调函数
js
const promise = new Promise((resolve, reject) => {
reject('哈哈')
})
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // 输出'哈哈'
})
2.2、当executor抛出异常时, 会调用错误(拒绝)捕获的回调函数
js
const promise = new Promise((resolve, reject) => {
throw new Error('手动抛出异常')
})
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // 输出'手动抛出异常'
})
2.3、catch方法的返回值。同上面then方法的返回值
js
const promise = new Promise((resolve, reject) => {
reject('233')
})
promise.then(res => {
console.log("res:", res)
return '12345'
}).catch(err => {
console.log("err:", err) // 输出'res: 233'
return "catch return value" // 同样是返回一个promise
}).then(res => {
console.log("res result:", res) // 输出'catch return value'
}).catch(err => {
console.log("err result:", err)
})
3、Promise对象上的finally方法
finally:表示Promise对象无论变成fulfilled还是rejected,最终都会执行的代码
finally方法是不接收参数的,因为前面无论是fulfilled还是rejected状态,都会执行
js
const promise = new Promise((resolve, reject) => {
resolve('成功的回调')
})
promise.then(res => {
console.log("res:", res) // 输出'res: 成功的回调'
return 111
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally code execute") // 输出'finally code execute'
return 222
}).then(res1 => {
console.log("res1:", res1) // 输出111
})
六、Promise类的方法
1、Promise.resolve()和Promise.reject()方法
可传入的值等同于resolve的参数,可以传入的值。
js
function foo() {
const obj = {name: 'wbb'}
let flag = true
return new Promise(resolve => {
if(flag) {
resolve(obj)
} else {
reject('报错了')
}
})
}
// 等同于
function foo() {
const obj = {name: 'wbb'}
let flag = true
if(flag) {
return Promise.resolve(obj)
} else {
return Promise.reject('报错了')
}
}
2、Promise.all()方法
all本身返回的是一个promise
按照传入的顺序返回
js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('222')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000)
})
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('444')
}, 4000)
})
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
}, 2000)
})
const p6 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('666')
}, 2000)
})
const p7 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('777')
}, 2000)
})
// 1、所有的状态都为fulfilled状态
Promise.all([p2, p1, p3, 'aaa', p4]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err)
})
// 4秒后按照顺序返回 ['222', '333', 'aaa', '111', '444']
// 2、Promise有一个状态为pending
Promise.all([p2, p1, p5]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err)
})
// 不会输出,all得等到所有的Promise的所有状态都为fulfilled,或者至少有一个状态为rejected才会输出
// 3、存在reject状态,不管Promise有没有一个状态为pending,都会输出
Promise.all([p2, p4, p5, p6, p7]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err)
})
// 2s后按照顺序输出错误信息['666', '777']
3、Promise.allSettled()方法
希望所有东西都有最终的结果,依次返回状态和值
不会走catch,以一个对象返回在then方法返回
js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2222')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('333')
}, 3000)
})
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1111')
}, 4000)
})
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
}, 2000)
})
// 1、所有的Promise状态都已经改变
Promise.allSettled([p2, p1, p3, 'aaa', p4]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err)
}) // 不会走catch,以一个对象返回在then方法返回
// 4秒后按照存入顺序返回
// [
// { status: 'fulfilled', value: '2222' },
// { status: 'fulfilled', value: '1111' },
// { status: 'rejected', reason: '333' },
// { status: 'fulfilled', value: 'aaa' },
// { status: 'fulfilled', value: '1111' }
// ]
// 2、Promise有一个状态为Pending
Promise.allSettled([p2, p1, p3, 'aaa', p5]).then(res => {
console.log(res)
})
// 不会返回结果
4、Promise.race()方法
只要有一个promise有结果,就结束。可以理解为赛跑,谁先有结果谁先输出。只输出一个结果。
js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('1111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2222')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
}, 3000)
})
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1111')
}, 4000)
})
// 只有有一个promise有结果,就结束
// 'aaa'是最快的
Promise.race([p2, p1, p3, 'aaa', p4]).then(res => {
console.log(res) // 立即输出'aaa'
}).catch(err => {
console.log('err', err)
})
Promise.race([p2, p1, p3, p4]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err) // 1秒后输出'err 1111'
})
5、Promise.any()方法
any要等到至少有一个fulfilled,才有对应的结果
js
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('1111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2222')
}, 500)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('3333')
}, 3000)
})
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('4444')
}, 4000)
})
// any要等到至少有一个fulfilled,才有对应的结果
Promise.any([p1, p3, p4, p2]).then(res => {
console.log(res)
}).catch(err => {
console.log('err', err)
})
// 500ms后输出'2222'
// 如果全是拒绝呢?
// 等到所有的拒绝全部执行完,才有结果.
// 输出: 'err AggregateError: All promises were rejected'