vue3 antd validator校验防抖的问题

https://github.com/zhou62396272/bucketName
这是我写的简单的模版案例,不嫌麻烦的可以跑一下项目试试帮我解决一下,🙏谢谢了!!

因为要求是输入即校验,需要调用接口,所以需要防抖
因为callback已废除,必须以Promise的形式才能触发validator,一般的debounce不能满足返回Promise的需求

现在的问题是:输入框不停输入时,以下代码防抖效果有了
但是因为每次输入都会把之前的那一次Promise取消掉,于是当我快速输入时,最后一次输入停了之后,其实是触发validator提示了,但是因为之前不停的在取消,所以页面来不及渲染,导致页面没有任何提示
如果是一个一个慢慢输入的话是能正常显示校验的

请问有什么解决方法吗

const abortPromise = (promise1) => {
  let abort
  const promise2 = new Promise((resolve, reject) => {
    abort = reject
  })
  const p = Promise.race([promise1, promise2])
  p.abort = abort
  return p
}

const debouncePromise = (success, fail, time) => {
  let promise
  return function(...rest) {
    if (promise && typeof promise.abort === 'function') {
      promise.abort()
    }
    const timeoutPromise = new Promise((resolve) => {
      setTimeout(() => {
        resolve(undefined)
      }, time)
    })
    promise = abortPromise(timeoutPromise)
    return promise.then(() => {
      return success(...rest)
    }, () => {
      return fail(...rest)
    })
  }
}

const fail = () => {
  console.log('由于防抖中断了第一次的请求')
  return Promise.resolve() // 忽视它暂时是正确的
}

const success = (rule, value, callback) => {
  console.log('请求后台接口')
  const reg = /^(?!(-|[-a-z0-9]*--|[-a-z0-9]*-$))[-a-z0-9]*[-a-z0-9]*$/
  if (value.length < 1) {
    return Promise.reject('名称不能为空')
  }
  if (value.length < 3 || value.length > 63) {
    return Promise.reject('请输入 3~63 个字符')
  } else if (!reg.test(value)) {
    return Promise.reject('只允许小写字母、数字、短横线(-),且不能以短横线开头或结尾')
  }
  // 调用接口查询是否重复
  return bucketNameCheck({
    bucketName: bucketInfo.bucketName
  }).then((res) => {
    if (res.available) {
      nameChecked.value = true
      return Promise.resolve()
    } else {
      // message.warning('该Bucket已存在,或被其他用户占用')
      return Promise.reject('该Bucket已存在,或被其他用户占用')
    }
  })
}

const check = debouncePromise(success, fail, 500)

const rule = {
  bucketNameRules() {
    return { validator: check, trigger: 'change', required: true }
  }
}

即时校验,不是要校验输入字符是否符号要求吗?为啥取消了……

最后一次校验,是整体校验;前面的即时校验,校验的是每个字符。你这样干有点奇怪欸

看了下代码,其实debouncePromise会在输入的时候不断触发,然后你的promise定义在debouncePromsie函数内(也就是闭包)这样每次执行这个方法的时候,你的promise都是undefined,自然不会触发promise.abort()。因为你传递给validator的是一个函数,虽然你的debouncePromise是用了闭包定义了promise,但是函数在执行的时候,它都是一个新的定义。

可以把let promise 的定义提升到外面

let promise;
const debouncePromise = (success, fail, time) => {
  
  return function(...rest) {
    if (promise && typeof promise.abort === 'function') {
      promise.abort()
    }
    const timeoutPromise = new Promise((resolve) => {
      setTimeout(() => {
        resolve(undefined)
      }, time)
    })
    promise = abortPromise(timeoutPromise)
    return promise.then(() => {
      return success(...rest);
    }, () => {
      return fail(...rest);
    })
  }
}

如果想全局使用这套防抖方案,可以参考这篇博客:https://blog.csdn.net/u010899138/article/details/107337700

return { validator: check, trigger: 'change', required: true } 这里的 trigger :'blur' 就不会有这个问题

试试forceUpdate强制更新?

<template>
  <input type="text" @change="changeHandle" v-model="value" />
</template>

<script setup>
import { ref } from 'vue'
const value = ref('')

const abortPromise = (promise1) => {
  let abort;
  const promise2 = new Promise((resolve, reject) => {
    abort = reject;
  })
  const p = Promise.race([promise1, promise2]);
  p.abort = abort
  return p
}

const debouncePromise = (success, fail, time) => {
  let promise;
  return function(...rest) {
    if (promise && typeof promise.abort === 'function') {
      promise.abort()
    }
    const timeoutPromise = new Promise((resolve) => {
      setTimeout(() => {
        resolve(undefined)
      }, time)
    })
    promise = abortPromise(timeoutPromise)
    return promise.then(() => {
      return success(...rest);
    }, () => {
      return fail(...rest);
    })
  }
}

const success = (...rest) => {
  console.log('请求后台接口', rest)
  return Promise.resolve('success')
}

const valid = (rule, value) => {
  const reg = /^(?![-])[a-z0-9-]{3,63}(?<![-])$/
  if (value.length < 3 || value.length > 63) {
    return Promise.reject('请输入 3~63 个字符')
  } else if (!reg.test(value)) {
    return Promise.reject('允许小写字母、数字、短横线(-),且不能以短横线开头或结尾')
  }
//调用接口查询是否重复
  return bucketNameCheck({
    bucketName: value
  }).then((res) => {
    if (res.available) {
      return Promise.resolve()
    } else {
      return Promise.reject('该Bucket已存在,或被其他用户占用')
    }
  })
}

const bucketNameCheck = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({
      available: Math.random() < 0.5,
    }), 1000)
  })
}

const fail = (...rest) => {
  console.log('由于防抖中断了第一次的请求', rest)
  return Promise.resolve({isPause: true}) // 忽视它暂时是正确的
}

const handle = debouncePromise(valid, fail, 2000)

const changeHandle = () => {
  console.log('123')
  handle('123', value.value).then(({isPause} = {}) => {
    if (isPause) {
      console.log("中断了请求,重新执行")
    } else {
      console.log('用户输入的没问题,此时标题框蓝色')
    }
  }).catch(() => {
    console.log('用户输入的有问题标题框红色')
  })
}
</script>



<style scoped>
li {
  color: #42b983;
}

label {
  margin: 0 0.5em;
  font-weight: bold;
}

code {
  background-color: #eee;
  padding: 2px 4px;
  border-radius: 4px;
  color: #304455;
}
</style>

防抖失效了吗