一个好习惯,先给结论

对promise进行封装改造,更方便、优雅地进行异步操作。完整代码库→:github

本文首发于blog.gis1024.com

这里才是引言

事情的起因是这样的:

在写苗刻地图项目时,遇到一个问题:表单验证怎么优雅进行,又或者说异步promise的异常怎么优雅处理。

element的表单验证举例来说,写一个登陆表单时,点击登陆前需要验证账号密码是否都填写了,验证成功后再发起登陆请求。

简短代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
....
<el-form ref="formRef" ...></el-form>
....
</template>

<script setup>
....
const formRef = ref(null);
await formRef.value.validate(); // 表单验证,比如验证用户名密码是否都填写
/*----- 验证成功后的操作 -----*/
// 比如发起请求
axios.post('xx/xx',{})
....
</script>

分析一下以上代码可以知道,验证成功后可以发起后续的登陆请求;验证不成功,则验证那行直接就抛异常了,后续登陆请求自然也就不会执行。

从最终结果上来说,这样的代码是符合整个功能逻辑的。但是,每次验证不成功时,没有处理抛出的异常。这样会带来两个问题:

  1. 控制台会飘红,打印抛出的异常;
  2. 如果前端做了一些异常监控的话,会将这些异常监控到并上传到服务器,造成脏数据事小,被领导觉得搞出bug来就问题大了。

一般的处理方式

作为一个老前端,这个问题肯定不会是现在才遇到,以前都是怎么处理的呢?

  1. element也提供回调的验证方式,使用回调方法进行验证
  2. 不使用await,直接使用promise.then().catch()
  3. 使用 try{} catch(e){}

1和2不必说了,很容易理解,但是会增加代码的缩进,增加后续心智负担,await用惯了自然不喜欢这种方式。

我们来看看3的处理方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
....
<el-form ref="formRef" ...></el-form>
....
</template>

<script setup>
....
try {
const formRef = ref(null);
await formRef.value.validate(); // 表单验证,比如验证用户名密码是否都填写
/*----- 验证成功后的操作 -----*/
// 比如发起请求
axios.post('xx/xx',{})
} catch(e){
console.log('验证不成功咯')
}
....
</script>

以上代码使用try catch包裹了一大坨,甚至还不如使用方法1和2。

我们需要一种更优雅的方式来实现

更优雅的处理方式

直接上代码了哈

to.js文件如下:

1
2
3
4
5
export function to(promise) {
return promise.then(res => [null, res]).catch(err => [err]);
}

export default to;

在上述的表单验证中,这么来写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
....
<el-form ref="formRef" ...></el-form>
....
</template>

<script setup>
import to from 'to.js'
....
const formRef = ref(null);
const [err,res] = await to(formRef.value.validate()); // 表单验证,比如验证用户名密码是否都填写
if(err) return;
/*----- 验证成功后的操作 -----*/
// 比如发起请求
axios.post('xx/xx',{})
....
</script>

分析下上面的的代码,是将promise进行了一层包裹,resolve时,返回两个值:异常和原本的结果值。

这样的好处是,既不需要包裹层层的try catch,也不需要使用.then回调增加缩进,更不会在控制台留下异常报错。如果有需要,还能对返回的异常进行业务对应的判断处理。

结语

使用一个简单的函数包装,即可实现promise的优雅异常处理。该方法不仅可以使用在表单验证中,所有使用await的地方,皆可使用该方法来包裹,代码逻辑非常清晰。

本文首发于blog.gis1024.com