Vue中如何在子组件触发事件后resolve Promise执行后续请求
问题描述
- 业务场景:用户发起数据请求前会先弹出验证码校验挑战,待校验通过后,验证码组件会触发携带请求所需token的事件。
- 核心疑问:
- 是否可以使用Promise等待验证码校验完成,再携带获取到的token发起请求?
- 是否可以通过监听子组件事件的方式来resolve对应的Promise?
- 如果该实现方式并非最佳实践,希望得到正确的实现思路。
- 已知前提:可以通过
v-on:verified监听子组件的校验完成事件,期望实现效果为:发起请求时自动触发验证码挑战,用户完成校验后自动继续执行后续请求逻辑。
当前实现代码
component.ts
import captchaComponent from '...'; @Component({ components: {captchaComponent} }) @Ref() readonly captcha!: captcha; function executeCaptchaChallenge(){ // 通过布尔prop触发子组件启动验证码挑战 this.runChallenge = true; const vm = this; return new Promise(resolve => { vm.captcha.$on('verified', token => {resolve(token)} }) } async function getData(){ const token = await this.executeCaptchaChallenge(); const response = await axios.post(URL, token); }
component.vue
<template> <div> <button @click="getData">GET DATA</button> <captchaComponent ref="captcha" /> </div> </template> <script src="component.ts"></script>
回答
用Promise封装验证码异步校验流程、配合async/await实现线性调用的思路完全可行,监听子组件事件resolve Promise也是合理的实现方式,但你当前的代码存在3个会引发线上问题的缺陷,必须修复:
- 事件监听器重复绑定:每次调用
executeCaptchaChallenge都会给验证码组件实例新增一个verified事件监听,且绑定后从未解绑。多次触发请求后会累积N个重复监听器,下次校验通过时会同时resolve多个历史Promise,导致重复发请求、逻辑错乱。 - 缺少异常分支处理:当前Promise只有成功回调没有失败/中断逻辑,如果用户主动关闭验证码、校验超时、校验失败,Promise会永远处于pending状态,后续逻辑卡死,还会造成内存泄漏。
- 版本兼容问题:直接在组件ref实例上调用
$on绑定事件的写法仅支持Vue2,Vue3移除了实例上的事件方法,该写法在Vue3环境下会直接报错。
适配现有Vue2写法的修正方案
核心是补充事件解绑逻辑和异常分支,代码如下:
function executeCaptchaChallenge(){ this.runChallenge = true; const captchaInstance = this.captcha; return new Promise((resolve, reject) => { // 校验成功处理 const handleSuccess = (token: string) => { clearListeners(); resolve(token); }; // 校验取消/失败处理 const handleFail = (err?: Error) => { clearListeners(); reject(err || new Error('验证码校验已取消')); }; // 统一清理逻辑:解绑所有事件、重置挑战状态 const clearListeners = () => { captchaInstance.$off('verified', handleSuccess); captchaInstance.$off('close', handleFail); captchaInstance.$off('error', handleFail); this.runChallenge = false; }; // 绑定事件监听 captchaInstance.$on('verified', handleSuccess); captchaInstance.$on('close', handleFail); captchaInstance.$on('error', handleFail); }) }
对应的getData方法需要补充错误捕获,避免未处理的Promise异常:
async function getData(){ try { const token = await this.executeCaptchaChallenge(); const response = await axios.post(URL, { token }); // 正常处理响应逻辑 } catch (err) { // 校验取消/失败场景,按需做提示即可 console.log('验证码未通过', err); } }
注意:示例中用到的close/error事件名请替换为你所用验证码组件实际暴露的取消、失败事件名。
通用最佳实践(Vue2/Vue3全兼容)
更推荐直接通过模板绑定组件事件,用组件实例变量暂存Promise的resolve、reject回调,完全不需要手动操作ref绑定/解绑事件,不会出现监听器泄漏问题,逻辑更清晰:
- 首先在组件上声明两个变量存Promise回调:
// 类组件中直接声明实例属性 private captchaResolve: ((token: string) => void) | null = null; private captchaReject: ((err?: Error) => void) | null = null;
- 模板上直接给验证码组件绑定事件,不需要手动操作ref:
<captchaComponent ref="captcha" v-if="runChallenge" @verified="handleCaptchaSuccess" @close="handleCaptchaFail" @error="handleCaptchaFail" />
- 补充对应方法:
// 验证码校验通过 function handleCaptchaSuccess(token: string) { this.runChallenge = false; if (this.captchaResolve) { this.captchaResolve(token); // 执行完后清空回调,避免重复触发 this.captchaResolve = null; this.captchaReject = null; } } // 验证码取消/校验失败 function handleCaptchaFail(err?: Error) { this.runChallenge = false; if (this.captchaReject) { this.captchaReject(err || new Error('验证码校验失败')); this.captchaResolve = null; this.captchaReject = null; } } // 触发验证码挑战 function executeCaptchaChallenge() { this.runChallenge = true; return new Promise((resolve, reject) => { this.captchaResolve = resolve; this.captchaReject = reject; }); }
getData方法的逻辑和之前修正后的版本一致,不需要调整。
内容的提问来源于stack exchange,提问作者user10745267
相关产品推荐
相关产品推荐

