My implementation of debounce axios request left the promise in pending state forever, is there a better way?

前端 未结 2 1913
悲&欢浪女
悲&欢浪女 2021-01-18 08:53

I need a simple debounce function with immediate always true.
Without resorting to lodash and with the help of Can someone explain the "debounce" function in J

相关标签:
2条回答
  • 2021-01-18 09:09

    But I am still not 100% sure about letting a promise stay in pending forever.

    I agree that it's not a good idea. A better approach would be to move the entire promise chain inside the debounced function.

    Another option would be to return a cached value when the debounced call does not trigger a new request. This would solve your problem that you always need to return a promise:

    function debounce(func, wait) {
        var timeout, value;
        return function() {
            if (!timeout) value = func.apply(this, arguments);
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                timeout = value = null;
            }, wait);
            return value;
        };
    }
    

    Of course that would mean that in some cases, multiple then handlers will be called when your request finishes. It depends on your application whether that is a problem or just superfluous work.

    The other question is that Promise Anti patterns suggested not creating unnecessary promise. But in my case creating a new promise seems necessary.

    Only one promise is necessary: when you create the never-resolved one. You can write that as

    function debounce(func, wait) {
        var timeout;
        const never = new Promise(resolve => {/* do nothing*/});
        return function() {
            const result = timeout ? never : func.apply(this, arguments);
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                timeout = null;
            }, wait);
            return result;
        };
    }
    

    Or at least avoid the .then(resolve).catch(reject) part. Better write

    function debounce(func, wait) {
        var timeout;
        return function() {
            return new Promise(resolve => {
                if (!timeout) resolve(func.apply(this, arguments));
    //                        ^^^^^^^
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    timeout = null;
                }, wait);
            });
        };
    }
    

    And should you consider to reject the promise in case that the timeout has not yet occurred (so that the calling code can handle the rejection), you don't need new Promise either:

    function debounce(func, wait) {
        var timeout;
        return function() {
            const result = timeout
              ? Promise.reject(new Error("called during debounce period"))
              : Promise.resolve(func.apply(this, arguments));
            clearTimeout(timeout);
            timeout = setTimeout(() => {
                timeout = null;
            }, wait);
            return result;
        };
    }
    
    0 讨论(0)
  • 2021-01-18 09:20

    In essence, you need to share result of your debounce function. In your case, thats a promise:

    const debouncedGetData = debounce(getData, 500)
    let promiseCount = 0
    let resultCount = 0
    test()
    
    function test() {
      console.log('start')
      callDebouncedThreeTimes()
      setTimeout(callDebouncedThreeTimes, 200)
      setTimeout(callDebouncedThreeTimes, 900)
    }
    
    function callDebouncedThreeTimes () {
       for (let i=0; i<3; i++) {
          debouncedGetData().then(r => {
            console.log('Result count:', ++resultCount)
            console.log('r', r)
          })
       }
    }
    
    function debounce(func, wait) {
        let waiting;
        let sharedResult;
        return function() {
            // first call will create the promise|value here
            if (!waiting) {
              setTimeout(clearWait, wait)
              waiting = true
              sharedResult = func.apply(this, arguments);
            }
            // else new calls within waitTime will be discarded but shared the result from first call
    
            function clearWait() {
              waiting = null
              sharedResult = null
            }
    
            return sharedResult
        };
    }
    
    function getData () {
      console.log('Promise count:', ++promiseCount)
      return new Promise((resolve, reject) => {
        setTimeout(() => {
           resolve(666)
        }, 1000)
      })
    }
    
    0 讨论(0)
提交回复
热议问题