为什么更推荐async而非promise?

149 阅读4分钟

我在 # 源哥带你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 })
        })
}

有时候,写八股抽象和骂八股的都是同一拨人