关于前端开发中一些写法的思考1 - Promise/async/await

226 阅读4分钟

在网上经常看别人写的文章,自己心血来潮也想写一点。主要思考内容是代码的方式。

感觉自己写的方式很少在别人的文章中或同事的代码中看到,不知道是好还是不好,发出来鉴定一下

(基本无关性能,都是从别人看的时候“更轻松”的角度出发的)

(代码例子都是vue,涉及到一些组件的方法,基本是ElementUI)

1. 使用.catch()代替trycatch来捕捉async/await的错误

async/await基本上算是一个很常用的功能了,经常被用来等待一些异步的任务。正常写业务代码的时候为了安全起见基本会对错误进行捕捉。我经常看到的捕捉方式基本上就是套一层trycatch。包括一些文章写的时候也都是使用这个方法来处理的,但是在实际开发中总觉得trycatch用起来不太顺手。

举一个比较极端的例子(校验表单并发送请求)

// 
const API = Promise.reject // 假设我是一个请求
const formData = {} // 假设我是表单数据

// 提交数据
const submit = async () => {
    try {
        const valid = await formRef.value.validate() // 这里提交失败会走到catch
        
        const res = await API(formData) // 这里接口404会走到catch
        
        // 业务代码
    } catch (e) {
        // 处理表单的错误
        // 处理接口的错误
    }    
}

遇到的问题

  1. 业务代码被多嵌套了一层,如果在方法内部还有一些其他操作,可能最后会连续嵌套两三层
  2. 所有的错误都被同一个catch处理,包括组件的错误、接口的错误等等
  3. 如果想解决上述的问题,也需要把valid字段和res字段写在trycatch的外层,并且改为let,也有点别扭

但是如果使用.catch()配合await一起使用


const API = Promise.resolve // 假设我是一个请求
const formData = {} // 假设我是表单数据

const submit = async () => {
    const isPass = await formRef.value.validate().catch(e => e)
    if (!isPass) return
    
    const { code } = await API(formData).catch(e => ({}))
    if (code != 200) return
    
    // 业务代码
}

使用.catch()的好处

  1. 所有代码没有额外的缩进和嵌套
  2. 所有变量在方法内部都可以访问
  3. 所有错误分别处理

2. 使用Promise来处理业务逻辑中的交互

这是一个业务逻辑和UI逻辑分离的想法

在写需求的时候,经常遇到的一个功能就是:

用户在点击了一个按钮后,先执行部分逻辑,然后展开一个弹窗,等弹窗的逻辑走完后再走后续的逻辑。

按照我常见的书写方式写了个例子,整体感觉就是业务逻辑直接被UI逻辑给打断了

举一个比较极端的例子(弹出同意协议,确定后进入下一步)

// 第一个触发的事件是 preLogin 也就是用户想登录

// 业务逻辑
const preLogin = () => {
    // 判断是否同意了协议
    if (!isAgree) {
        isDialogShow.value = true 
        return // 未通过协议就打开弹窗并停止登录
    }
    realLogin() // 如果通过了协议就正常登录
}
const realLogin = () => {
    // 登录
}

// 弹窗相关的代码
const isAgree = ref(false)
const isDialogShow = ref(false)
const handleAgree = () => isAgree.value = true // 切换协议状态
const handleSubmit = () => {
    preLogin() // 点击了弹窗的【确定】按钮,再次执行登录
}

如果是自己开发自己维护,整体都没有问题。

但是把例子中的代码直接给别人看

经常会遇到需要多个方法来回跳着看的问题。

先看到preLogin执行了,然后有个isAgree的判断。这个时候就需要跳到下面看isAgree是什么,isDialogShow是什么,然后再看弹窗绑定的方法是不是handleSubmit,最后从handleSubmit回到preLogin

如果业务逻辑涉及到二次弹窗,整个逻辑会再被拆分,直接看的头都大了

使用Promise

// 第一个触发的事件是 login 也就是用户想登录

// 业务逻辑
const login = async () => {
    const isAgree = await checkAgree() // 校验是否同意协议并开始等待
    if (!isAgree) return
    // 通过协议 准备登录
    // 登录的代码
}

// 弹窗逻辑
let promiseCallback = () => {} // Promise的回调,绑定在弹窗的确定按钮上
const isAgree = ref(false) // 是否同意协议
const isDialogShow = ref(false) // 弹窗是否暂时
const checkAgree = () => {
    if (!isAgree) {
        isDialogShow.value = true // 打开弹窗
        return new Promise((resolve, reject) => promiseCallback = resolve) // 保存resolve事件,等待弹窗结束
    }
    return Promise.resolve(true)
}

同样如果是其他开发看这段代码,正常从login方法开始看。1.校验协议 2.完成登录。这段逻辑就结束了。根本不用去看弹窗这段逻辑。

这样写会不会更清晰一些呢?不知道大家怎么看。