先写个debounce

  const _debounce = (fn, delay, immediate=false) => {
      let timer;
      return (...args) => {
          const callNow = immediate && !timer;
          clearTimeout(timer);
          timer = setTimeout(() => {
              timer = null; 
              if (!immediate) fn(...args); 
          }, delay);
          if (callNow) fn(...args);
      };
  };

debounce with promise

  • 如果我在拖动鼠标的时候,触发debounce, 但是其中的函数是去调用某个api,这样这个函数就是异步的,是一个promise, 如果这个时候我想只执行最后一个promise,那我的debounce改怎么写,为什么这样问,因为我们看到lodash里边对调用的fn 其实是有一个判断是不是函数的步骤的if (typeof func !== 'function') {throw new TypeError('Expected a function')} 所以,如果是个promise我们需要重写一下,逻辑也是很简单的,如下:
const debounce_promise_last = (fn, delay) => {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    return new Promise((resolve) => {
      timer = setTimeout(
        () => resolve(fn(...args)),
        delay,
      );
    });
  };
}
  • 如果我还是需要所有的promise,每个promise之间有一个ms的间隔
function debounce_promise_all(fn, delay = 0) {
  let timer = null;
  let resolves = [];
  return function (...args) {    
    clearTimeout(timer);
    timer = setTimeout(() => {
      let result = fn(...args);
      resolves.forEach(r => r(result));
      resolves = [];
    }, delay);

    return new Promise(r => resolves.push(r));
  };
}
  • 其实如果是api调用,也有一种常见情况就是我们用throttle来保证一段时间内只有一个调用, 这里其实还有更复杂的情况,比如cancel情况,retry情况,特别retry的时候,是不是用指数退化算法 当然,如果promise量很大的时候,比如我们碰到过一次1000多个promise,那要做的更复杂,需要考虑如下情况:
    • 第一,给promise分组,我们一般20个promise放一组
    • 第二,每个promise有一个唯一ID,我们结合task给了一个唯一的ID
    • 第三,记录每个promise的日志,方便错误追溯,我们放splunk log里
    • 第四,是否需要cancel
    • 第五,retry的逻辑,因为我们api是ajax请求,结合指数退化算法来做重试的时延是比较合适的
  • 这里代码就和具体业务相关了,就不贴出来了