我在 # 源哥带你CodeReview01 一问中,提到了Promise地狱,还是有很多小伙伴觉得这个问题有些上纲上线,这里在细化一下原因
首先排除代码简洁这句话没什么营养的内容,这个内容主要是Promise的最佳实践
原因概览
基于以下代码,可能因为各种原因,出现逻辑问题
api({ a: 1 })
.then(() => {
api({ a: 2 })
.then(() => {
// ....
})
})
.catch((e) => {
Message.error(e.message)
})
然而,无论上面玩的有多花,基于以下代码,写法都差不多
async function a() {
try {
await api({ a: 1 })
await api({ a: 2 })
await api({ a: 3 })
} catch (error) {
}
}
像第一类的代码,特别考眼力,也就逐渐形成了,先把这种代码改为async,然后在看业务逻辑漏洞的习惯
这不是八股,是业务抽象
我知道有人觉得这是没什么用的八股,但他实际上是我们内部cr的业务抽象,下面所有问题,都在我经历过的项目中找到过原型,如果拿他用来面试,通过率感人,属于那种用过就会,不用不会的范畴,我个人的意见永远是能用async就不要用Promise
请指出以下代码的问题1
api({ a: 1 })
.then(() => {
api({ a: 2 })
.then(() => {
// ....
})
})
.catch((e) => {
Message.error(e.message)
})
主要问题是api({ a: 2 })的错误没有被拦截
业务代码里,大概率会改成下面的样子
api({ a: 1 })
.then(() => {
api({ a: 2 })
.then(() => {
// ....
})
.catch((e) => {
Message.error(e.message)
})
})
.catch((e) => {
Message.error(e.message)
})
两个catch一模一样
请指出以下代码的问题2
需求是1,2,3 3个接口需要依次触发
api({ a: 1 })
.then(() => {
api({ a: 2 })
})
.then(() => {
api({ a: 3 })
.then(() => {
})
})
.catch((e) => {
Message.error(e.message)
})
大多数会改成下面这种格式,然后catch会写三遍 【函数嵌套与三遍catch就是回调地狱】
api({ a: 1 })
.then(() => {
api({ a: 2 })
.then(() => {
api({ a: 3 })
.then(() => {
})
})
})
.catch((e) => {
Message.error(e.message)
})
Promise的最佳实践
我们用async/await,更有可能代码如下,而这种方式对应的就是Promise的最佳实践
最重要的是catch只有一遍
async function a() {
try {
await api({ a: 1 })
await api({ a: 2 })
await api({ a: 3 })
} catch (error) {
}
}
Promise的最佳实践1
上面的代码跟下面的是类似的,关键点是那个return
api({ a: 1 })
.then(() => {
return api({ a: 1 })
})
.then(() => {
return api({ a: 2 })
})
.then(() => {
return api({ a: 3 })
})
.catch((e) => {
Message.error(e.message)
})
Promise的最佳实践2
最终流程的结束,一定在最后的then/catch中,这也意味着中间的api,即使需要catch,也需要向外抛异常,他可以预防没有捕获catch的问题
相似的约束类似于一个函数只有一个入口和出口[当然,这个没人能做到 /dog]
// 假设需要单独处理第二个api
api({ a: 1 })
.then(() => {
return api({ a: 2 })
.catch(() => {
throw new Error('无权限')
})
})
.then(() => { })
.catch((error) => {
// 根据error判断
})
// 利用传递的特性,与上面的语义一样
api({ a: 1 })
.then(() => {
return api({ a: 2 })
})
.catch(() => {
throw new Error('无权限')
})
.then(() => { })
.catch((error) => {
// 根据error判断
})
个人看法
Promise是创建任务链和执行任务链两个过程,只要能保证这两个基础的最佳实践,想怎么做都行
但大多数时候,是无法保证基础的最佳实践,下面的八股全是我们业务中代码的抽象,有兴趣的可以玩一玩
promise与async的语义平替
看一道八股题,这次不是说出console的顺序,而是用async/await重写,且保持console的顺序,需要改成什么样?
function a() {
console.log(1)
api({ a: 1 })
.then(() => {
console.log(2)
api({ a: 2 })
.then(() => {
console.log(3)
})
.catch(() => {
console.log(4)
})
console.log(5)
})
.catch(() => {
console.log(6)
})
console.log(7)
}
- 答案
一个看起来很简单的答案,但事情还没有完,他有变体
async function a() {
console.log(1)
console.log(7)
try {
await api({ a: 1 })
console.log(2)
console.log(5)
try {
await api({ a: 2 })
console.log(3)
} catch (error) {
console.log(4)
}
} catch (error) {
console.log(6)
}
}
取消catch
注意,console.log(4) 被取消了
function a() {
console.log(1)
api({ a: 1 })
.then(() => {
console.log(2)
api({ a: 2 })
.then(() => {
console.log(3)
})
// .catch(() => {
// console.log(4)
// })
console.log(5)
})
.catch(() => {
console.log(6)
})
console.log(7)
}
- 答案
注意,答案是注释console.log(4),并抛出reject异常,而不是取消第二个api的try/catch,接下来上强度
注:window.onrejectionhandled 会接受到错误
async function a() {
console.log(1)
console.log(7)
try {
await api({ a: 1 })
console.log(2)
console.log(5)
try {
await api({ a: 2 })
console.log(3)
} catch (error) {
Promise.reject(error)
// console.log(4)
}
} catch (error) {
console.log(6)
}
}
添加一个then
注意那个5.5,他是添加到第二个api.then后面的
function a() {
console.log(1)
api({ a: 1 })
.then(() => {
console.log(2)
api({ a: 2 })
.then(() => {
console.log(3)
})
// .catch(() => {
// console.log(4)
// })
console.log(5)
})
.then(()=>{
console.log(5.5)
})
.catch(() => {
console.log(6)
})
console.log(7)
}
- 答案
八股题这东西,有心算无心,到这,80%已经挂了
async function a() {
console.log(1)
console.log(7)
try {
await api({ a: 1 })
console.log(2)
console.log(5)
console.log(5.5)
try {
await api({ a: 2 })
console.log(3)
} catch (error) {
// console.log(4)
}
} catch (error) {
console.log(6)
}
}
添加一个return
第二个api返回的是return
function a() {
console.log(1)
api({ a: 1 })
.then(() => {
console.log(2)
return api({ a: 2 })
.then(() => {
console.log(3)
})
// .catch(() => {
// console.log(4)
// })
console.log(5)
})
.then(()=>{
console.log(5.5)
})
.catch(() => {
console.log(6)
})
console.log(7)
}
- 答案
async function a() {
console.log(1)
console.log(7)
try {
await api({ a: 1 })
console.log(2)
// console.log(5) 不会被执行
try {
await api({ a: 2 })
console.log(3)
console.log(5.5)
} catch (error) {
// console.log(4)
}
} catch (error) {
console.log(6)
}
}
箭头函数
不描述了,看起来就是一个配合箭头函数考眼力的游戏,其核心还是return的问题
function a() {
api({ a: 1 })
.then(() => api({ a: 2 }))
api({ a: 1 })
.then(() => {
api({ a: 2 })
})
}
有时候,写八股抽象和骂八股的都是同一拨人