vue.js 搜索输入的自定义回调堆栈

wljmcqd8  于 2023-04-21  发布在  Vue.js
关注(0)|答案(1)|浏览(145)

有一个使用普通去抖动函数(Vue组件)的处理程序的搜索输入:

created() {
      this.handleChange = debounce(async (ev) => {
        this.loading = true;
        const { target: { value: search } } = ev;
        if (search) {
          const response = await searchRepos({ search });
          const repos = await response.json(response);
          console.log(search);
          console.log(repos?.items);
          this.repos = repos?.items;
        } else {
          this.repos = [];
        }
        this.loading = false;
      }, 500);
    }

去抖动

const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback.apply(null, args);
    }, wait);
  };
}

工作正常,但这里有一个时刻。回调并不等待对方,它可能会创建一个情况,当前一个回调返回值后,下一个回调。
例如,当用户擦除倒数第二个符号时发送请求,在此用户擦除最后一个符号时,其回调结束得更快,因为没有请求。然后前一个回调返回值,并显示无效信息。坚韧发送请求,也不能保证我们在前一个之后收到响应。
我的解决方案是像这样改进去抖动:

export const debounce = (callback, waitTime) => {
  let timeoutId = null;
  let isPreviousPerforming = false;
  let callbacksOrder = [];
  const performRest = async () => {
    for (const order of callbacksOrder) {
      await callback.apply(null, [order.ev]);
    }
    callbacksOrder = []
  };
  return (ev) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(async () => {
      if (isPreviousPerforming) {
        callbacksOrder.push({ ev });
        return;
      }
      isPreviousPerforming = true;
      await callback.apply(null, [ev]);
      await performRest();
      isPreviousPerforming = false;
    }, waitTime);
  };
}

但这里又是一个时刻:在forcallbacksOrder = []之间,我们可能会收到一个事件,这个事件将被推送到callbacksOrder,我们将删除它。
问题:以这种方式处理搜索输入有什么可能的解决方案或最佳实践?
P.S. v-debounce的工作原理与普通debounce类似。
回调应连续解决。

l3zydbqr

l3zydbqr1#

如果你不需要结果,我不会等待所有的请求。相反,你可以在启动一个新的请求时用AbortController中止运行请求,这会拒绝所有当前的等待。但是你必须在你的searchRepos()中构建这个。
类似这样的东西应该可以工作:

const debounceWithAbort = (callback, wait) => {
  let timeoutId = null;
  let controller = null; // <--- add controller reference
  return (...args) => {
    window.clearTimeout(timeoutId);
    if (controller) { // <--- abort running fetches
      controller.abort()
    }
    timeoutId = window.setTimeout(() => {
      controller = new AbortController // <--- create controller
      callback.apply(null, [controller.signal, ...args]); // <--- pass in signal
    }, wait);
  };
}

然后你可以将信号传递给searchRepos()

this.handleChange = debounceWithAbort(async (signal, ev) => { // <--- take signal
        const { target: { value: search } } = ev;
        if (!search) {
          this.repos = [];
          return
        }
        this.loading = true;
        try{
          const response = await searchRepos({ search }, signal); // <--- pass signal to request and use it there
          const repos = await response.json(response);
        } catch (e) {
          return // <--- aborted
        }
        this.repos = repos?.items;
        this.loading = false;
      }, 500);
    }

你在searchRepos()中对signal做什么取决于你如何发送请求,但我认为现在AbortController在任何地方都受到支持。

相关问题