写在前面
一个月前说好要整理对promise的理解和手写promise代码,到现在都还没来得及兑现,是时候兑现自己的promise了。本文是结合B站的视频和自己的理解对promise的相关知识点进行梳理和总结,如果有需要,大家可以通过传送门自行查阅。后续会继续兑现自己的promise,抽空整理手写promise代码的文章。
promise相关知识点理解教程(视频):杰哥课堂-2020最好理解的Promise教程
promise方法的理解(视频): Promise方法
为什么需要promise
假设有这么一个需求场景,首先需要通过发起请求获取id,再通过获得的id请求接口获取用户名,再根据获得的用户名发起请求获取用户的邮箱。如果使用ajax进行请求,需要在第一个请求的success成功回调中发起第二个请求,再通过在第二个请求的success成功回调中发起第三个请求。如果还需要请求其它数据,这样层层嵌套就形成了回调地狱,promise就是为了解决回调地狱而生,使我们代码更加优雅。
promise的基本使用
- Promise是一个构造函数,通过
new关键字实例化对象。 1. 语法:new Promise((resolve, reject) => {})
2. 接受一个函数作为参数
3. 在参数函数中接受两个参数resolve和reject
- Promise实例有两个属性: 1. state:状态
2. result: 结果
promise的状态(为了区分then方法中的哪个函数被执行)
promise的状态
第一种状态: pending(准备,待解决,进行中)
第二种状态: fulfilled(已完成,成功)
第三种状态: rejected(已拒绝,失败)
promise状态的改变
如果不调用resolve和reject,返回的Promise实例的状态为pending,但是可以通过调用resolve()和reject()改变当前promise对象的状态。调用resolve(),当前promise对象的状态会改为fulfilled,调用reject(),当前promise对象的状态会变化为rejected。
const p = new Promise((resolve, reject) => {
// resolve(): 调用函数, 使当前promise对象的状态改成fulfilled
// reject(): 调用函数, 使当前promise对象的状态改成rejected
resolve()
// reject()
})
console.dir(p)
注意: promise状态的改变是一次性的。 这句话的意思就是说,如果尝试多次调用函数去改变当前Promise对象的状态,当前Promise对象的状态只会受第一次调用的函数的影响。比如:
const p = new Promise((resolve, reject) => {
resolve()
reject()
})
console.dir(p) //状态为fulfilled
promise状态的补充
promise的状态不改变, 即promise的状态为pending时,不会执行then里面的方法。
// 如果promise的状态不改变, then里的方法不会执行
new Promise((resolve, reject) => {
}).then((value) => {
console.log('成功')
}, (reason) => {
console.log('失败')
})
promise的结果(通过形参去接收)
如果不调用函数,返回的Promise对象的结果默认为undefined;如果调用了函数,且在函数中传递了参数,那么返回的Promise对象的结果就是函数中所传递的参数。
const p = new Promise((resolve, reject)=> {
// 通过调用resolve, 传递参数, 改变当前promise对象的结果
resolve('成功的结果')
//reject('失败的结果')
})
console.dir(p)
promise的原型对象方法
then方法
有两个函数参数,分别在成功时和失败时进行调用。返回值同样是一个Promise对象,无论then方法前面的Promise对象的状态是什么,then方法返回的状态都是pending,因此可以对Promise进行then方法的链式调用。
const p = new Promise((resolve, reject)=> {
// 通过调用resolve, 传递参数, 改变当前promise对象的结果
resolve('成功的结果')
//reject('失败的结果')
})
// then方法函数
// 参数
// 1. 是一个函数
// 2. 还是一个函数
// 返回值: 是一个promise对象
p.then(()=>{
// 当promise的状态是fulfilled时, 执行
console.log('成功时调用')
}, () => {
// 当promise的状态是rejected时, 执行
console.log('失败时调用')
})
console.dir(p) //打印成功时调用
const p = new Promise((resolve, reject)=> {
// 通过调用resolve, 传递参数, 改变 当前promise对象的 结果
//resolve('123')
reject('失败的结果')
})
// then方法函数
// 参数
// 1. 是一个函数
// 2. 还是一个函数
// 返回值: 是一个promise对象
p.then((value)=>{
// 当promise的状态是fulfilled时, 执行
console.log('成功时调用', value)
}, (err) => {
// 当promise的状态是rejected时, 执行
console.log('失败时调用', err)
})
console.dir(p) //打印失败的结果 失败时调用
then方法的补充
书接上文,前面已经提到promise对象的then方法返回的是一个pending状态的promise。那么该如何去改变这个promise的状态呢?话不多说,上代码:
- 将状态由pending改为fulfilled
const p = new Promise((resolve, reject) => {
// 如果promise的状态不改变, then里的方法也不会执行
//如果此处不调用resolve()方法,在p.then()方法中也不会调用成功回调
//继而也不会改变promise对象的then方法返回的状态
//因此,如果想改变promise对象的then方法返回的状态,这句话必须得写上
resolve()
})
const t = p.then((value) => {
console.log('成功')
// 在成功回调中,使用return可以将t实例的状态从pending改成fulfilled,并传递成功时的参数
//在返回的Promise对象的成功回调中,可以使用value进行接收所传递的参数
return 123
}, (reason) => {
console.log('失败')
})
t.then((value) => {
console.log('成功2', value)
}, (reason) => {
console.log('失败')
})
- 将状态由pending改为rejected 如果在then方法中, 出现代码错误, 会将返回的promise实例改为rejected状态
// 如果promise的状态不改变, then里的方法不会执行
const p = new Promise((resolve, reject) => {
resolve()
})
const t = p.then((value) => {
console.log('成功')
// 使用return可以将t实例的状态改成fulfilled
//return 123
// 如果这里的代码出错, 会将t实例的状态改成rejected
console.log(a)
}, (reason) => {
console.log('失败')
})
t.then((value) => {
console.log('成功2', value)
}, (reason) => {
console.log('失败', reason)
})
catch方法
1.catch中的参数函数在promise对象的状态改为rejected时会执行触发。
2.promise执行体中出现代码错误时,也会执行。
const p = new Promise((resolve, reject) =>{
// reject()
// console.log(a)
throw new Error('出错了')
})
// 思考: catch中的参数函数在什么时候被执行?
// 1. 当promise的状态改为rejected时, 被执行
// 2. 当promise执行体中出现代码错误时, 被执行
p.catch((reason) => {
console.log('失败', reason)
})
console.log(p)
promise最常见的写法
上文已经提到过,在then方法中可以接收成功和失败的回调。但因为有了catch方法这一件小棉袄,我们最常见的写法是将promise的成功回调放在then方法中执行,将promise的失败回调放在catch方法中执行。
new Promise((resolve, reject) => {
})
.then((val) => {
//成功时被执行
console.log(val)
})
.catch((err) => {
//失败时被执行
console.log(err)
})
all方法
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
用法: const p =Promise.all([p1, p2, p3]); //p1、p2、p3都是promise实例
注意事项:
1.Promise.all()方法接受一个Promise实例数组作为参数,方法的返回值也是一个promise。这就意味着可以在Promise.all()后使用then方法接收成功回调,使用catch方法接收失败回调。
2.只有Promise实例数组的状态都为fulfilled时,才会执行Promise.all()后的成功回调。如果Promise实例数组中有一个状态为rejected,就会马上执行Promise.all()后的失败回调,返回第一个rejected的结果,而不会执行成功回调。
- 成功回调的结果
let p1 = new Promise((resolve, reject) => {
setTimeOut(resolve, 2000, 'p1')
})
let p2 = new Promise((resolve, reject) => {
setTimeOut(resolve, 1000, 'p2')
})
let p3 = new Promise((resolve, reject) => {
setTimeOut(resolve, 3000, 'p3')
})
let pAll = Promise.all([p1, p2, p3])
pAll
.then(success => {
console.log(success)
})
.catch(err => {
console.log(err)
})
//返回["p1","p2","p3"]
//返回值为一个数组
//返回值的顺序与Promise.all方法中的调用顺序是一致的,而与异步请求的时间快慢无关。
- 失败回调的结果
let p1 = new Promise((resolve, reject) => {
setTimeOut(reject, 2000, 'p1')
})
let p2 = new Promise((resolve, reject) => {
setTimeOut(resolve, 1000, 'p2')
})
let p3 = new Promise((resolve, reject) => {
setTimeOut(resolve, 3000, 'p3')
})
let pAll = Promise.all([p1, p2, p3])
pAll
.then(success => {
console.log(success)
})
.catch(err => {
console.log(err)
})
//返回值为p1
//一旦有一个promise失败,会直接返回触发失败的回调函数,而不再理会其它的promise是否完成
race方法
写法和all方法大同小异,方法的返回同样是一个promise对象。当然也可以继续使用then方法触发成功回调,使用catch方法触发失败回调。记住一个原则即可: 谁先完成就根据完成的状态和结果返回谁。
用法: const p =Promise.race([p1, p2, p3]); //p1、p2、p3都是promise实例
因为和all方法相差无几,这里就不举栗了。
写在后面的promise
后续会陆续抽空整理手写promise代码、自定义Zoomout浏览组件、全局防抖以及sku属性筛选,过一遍购物车的逻辑。自己直到现在都没完整地通读一遍vue文档,必须强迫自己通读且多次通篇浏览vue文档(vue全家桶)和vue-element-admin文档,看vue源码,学习token以及axios封装。还是很菜,还是有很多地方学要学习。
学习&总结