xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

如何使用 js 实现一个 debounce 函数 All In One

如何使用 js 实现一个 debounce 函数 All In One

1. 原理

防抖: 是指在指定的单位时间内,如果重复触发了相同的事件,则取消上一次的事件,重新开始计时!

2. 实现方式


"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-10-01
 * @modified
 *
 * @description 防抖 & debounce
 * @difficulty Easy Medium Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link
 * @solutions
 *
 * @best_solutions
 *
 */

const log = console.log;

// 防抖: 是指在指定的单位时间内,如果重复触发了相同的事件,则取消上一次的事件,重新开始计时!
function debounce(callback, timer = 1000) {
  let id = null;
  return function() {
    clearTimeout(id);
    id = setTimeout(() => {
      callback();
    }, timer);
  }
}

const cb = () => log(`callback function!`)

const test = debounce(cb, 3000);

log(`test`, test);
test();
setTimeout(() => {
  log(`test2`);
  test();
}, 1000);
setTimeout(() => {
  log(`test3`);
  test();
}, 2000);



/*

$ node debounce.js

test [Function]
test2
test3
callback function!

*/



this & arguments


"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-10-01
 * @modified
 *
 * @description 防抖 & debounce
 * @difficulty Easy Medium Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link
 * @solutions
 *
 * @best_solutions
 *
 */

const log = console.log;

// 防抖: 是指在指定的单位时间内,如果重复触发了相同的事件,则取消上一次的事件,重新开始计时!
function debounce(callback, timer = 1000) {
  let id = null;
  return function() {
    // const args = [...arguments];
    const args = arguments;
    const that = this;
    // function (this, arguments)
    clearTimeout(id);
    id = setTimeout(function () {
      log(`that`, that)
      log(`this`, this)
      log(`args`, args)
      callback.call(that, [...args]);
    }, timer);
    // Arrow Function (this)
    // id = setTimeout(() => {
    //   callback();
    // }, timer);
  }
}

// const cb = () => log(`callback function!`);
const cb = (args) => log(`callback function!`, args);

const test = debounce(cb, 3000);

log(`test`, test);
test(`args = arguments`, 1);

setTimeout(() => {
  log(`test2`);
  test(`args = arguments`, 2);
}, 1000);
setTimeout(() => {
  log(`test3`);
  test(`args = arguments`, 3);
}, 2000);



/*

$ node debounce.js

test [Function]
test2
test3
callback function!

*/



/*

$ node debounce.js

test [Function]
test2
test3
that undefined
this Timeout {
  _idleTimeout: 3000,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 2044,
  _onTimeout: [Function],
  _timerArgs: undefined,
  _repeat: null,
  _destroyed: false,
  [Symbol(refed)]: true,
  [Symbol(asyncId)]: 11,
  [Symbol(triggerId)]: 7
}
args [Arguments] { '0': 'args = arguments', '1': 3 }
callback function! [ 'args = arguments', 3 ]

*/



function debounce_leading(func, timeout = 300){
  let timer;
  return (...args) => {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);
  };
}

https://www.freecodecamp.org/news/javascript-debounce-example/


function debounce(func, wait, immediate) {
  var timeout;
  return function (...args) {
    var context = this;
    // var args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) {
         func.apply(context, args);
      }
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (immediate && !timeout) {
      func.apply(context, args);
    }
  };
};

https://www.educative.io/answers/how-to-use-the-debounce-function-in-javascript

3. 总结

js debounce with arguments

function test (a, b, c, d)  {
  // ES5  slice & arguments
  const args = Array.prototype.slice.call(arguments, 0); 
  console.log(`args =`, args); 
} 

test(1,2,3);
// args = (3) [1, 2, 3]

test(1,2,3,4,5);
// args = (5) [1, 2, 3, 4, 5]

function test (a, b, c, d)  {
  // ES6 ...rest, ...spread
  const args = [...arguments]; 
  console.log(`args =`, args); 
}

test(1,2,3);
// args = (3) [1, 2, 3]

test(1,2,3,4,5);
// args = (5) [1, 2, 3, 4, 5]

ES6 js debounce function with params without using arguments



// function debounce(func) {
//   var id;
//   return function (args) {
//     console.log(`args 1 =`, args);
//     var context = this;
//     // var args = arguments;
//     clearTimeout(id);
//     id = setTimeout(() => {
//       // func.apply(context, args);
//       // func.apply(context, [...args]);
//       func.apply(context, ...args);
//       // Uncaught TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function
//     });
//   };
// };

function debounce(func, delay) {
  let id;
  return function (...args) {
    console.log(`\nrest args =`, args);
    console.log(`rest ...args =`, ...args);
    console.log(`rest [...args] =`, [...args]);
    let args1 = arguments;
    let args2 = Array.prototype.slice.call(arguments, 0);
    console.log(`args1 =`, args1);
    console.log(`args2 =`, args2);
    let context = this;
    // let that = this;
    clearTimeout(id);
    id = setTimeout(() => {
      func.apply(context, args);
      // func.apply(context, [...args]);
      // func.apply(context, ...args);
      // Uncaught TypeError: CreateListFromArrayLike called on non-object
      func.call(context, ...args2);
    }, delay);
  };
};


function test (a, b, c, d) {
  const args = [...arguments];
  console.log(`test args =`, args);
}

const fn = debounce(test, 1000);

fn(1,2,3);
// fn(1,2,3,4);
fn(1,2,3,4,5);


"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2022-10-18
 * @modified
 *
 * @description
 * @description
 * @difficulty EMedium
 * @time_complexity O(n)
 * @space_complexity O(n)
 * @augments
 * @example
 * @link https://www.cnblogs.com/xgqfrms/p/13849482.html
 * @solutions
 *
 * @best_solutions
 *
 */

const log = console.log;

// function debounce(func) {
//   var id;
//   return function (args) {
//     console.log(`args 1 =`, args);
//     var context = this;
//     // var args = arguments;
//     clearTimeout(id);
//     id = setTimeout(() => {
//       // func.apply(context, args);
//       // func.apply(context, [...args]);
//       func.apply(context, ...args);
//       // Uncaught TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function
//     });
//   };
// };

// function debounce(func, delay) {
//   let id;
//   // ✅ ...rest 保证在不使用 arguments 的情况下,也可以传入不定数量的参数
//   return function (...args) {
//     console.log(`\nrest args =`, args);
//     console.log(`rest ...args =`, ...args);
//     console.log(`rest [...args] =`, [...args]);
//     let args1 = arguments;
//     let args2 = Array.prototype.slice.call(arguments, 0);
//     console.log(`args1 =`, args1);
//     console.log(`args2 =`, args2);
//     let context = this;
//     // let that = this;
//     clearTimeout(id);
//     id = setTimeout(() => {
//       // ✅ apply 接受参数数组 [arg1, arg2, ...]
//       func.apply(context, args);
//       // func.apply(context, [...args]);
//       // func.apply(context, ...args);
//       // Uncaught TypeError: CreateListFromArrayLike called on non-object
//       // ✅ call 接受参数列表 (arg1, arg2, ...)
//       func.call(context, ...args2);
//     }, delay);
//   };
// };

const debounce = (func, delay) => {
  let id;
  // ✅ ...rest 保证在不使用 arguments 的情况下,也可以传入不定数量的参数
  return async (...args) => {
    console.log(`\nrest args =`, args);
    console.log(`rest ...args =`, ...args);
    console.log(`rest [...args] =`, [...args]);
    let context = this;
    // let that = this;
    clearTimeout(id);
    // id = setTimeout(() => {
    //   // ✅ apply 接受参数数组 [arg1, arg2, ...]
    //   func.apply(context, args);
    //   // func.apply(context, [...args]);
    //   // ✅ call 接受参数列表 (arg1, arg2, ...)
    //   // func.call(context, ...args);
    // }, delay);
    const promise = new Promise((resolve, reject) => {
      id = setTimeout(() => {
        resolve(func.apply(context, args));
      }, delay);
    });
    // return promise;
    const result = await(promise);
    console.log(`result`, result);
    return result;
    // js how to get setTimeout inner function return value ✅ promise wrap  & async / await
  };
};

// function test (a, b, c, d) {
//   const args = [...arguments];
//   console.log(`test args =`, args);
// }

// const fn = debounce(test, 1000);

// fn(1,2,3);
// // fn(1,2,3,4);
// fn(1,2,3,4,5);

// 测试用例 test cases
const testCases = [
  {
    input: [1,2,3],
    result: '1,2,3',
    desc: 'value equal to "1,2,3"',
  },
  {
    input: [1,2,3,4],
    result: '1,2,3,4',
    desc: 'value equal to "1,2,3,4"',
  },
  {
    input: [1,2,3,4,5],
    result: '1,2,3,4,5',
    desc: 'value equal to "1,2,3,4,5"',
  },
];

function test (a, b, c, d) {
  const args = [...arguments];
  console.log(`test args =`, args);
  return args;
}

const func = debounce(test, 1000);

log(`func =`, func);
// func = [AsyncFunction (anonymous)]
// func = Promise { [Function (anonymous)] }


(async () => {
  for (const [i, testCase] of testCases.entries()) {
    async function testCaseAsyncFunc(i, testCase) {
      const result = await func(...testCase.input);
      log(`result =`, result);
      // result = Promise { <pending> }
      // TypeError: func is not a function
      log(`test case ${i} result: `, result.join() === testCase.result ? `✅ passed` : `❌ failed`, result);
      // log(`test case ${i} =`, testCase);
    }
    await testCaseAsyncFunc(i, testCase);
  }
})();

refs

How to get the return value of the setTimeout inner function in js All In One / 在 js 中如何获取 setTimeout 内部函数的返回值

https://www.cnblogs.com/xgqfrms/p/16806941.html

js debounce & throttle All In One

https://www.cnblogs.com/xgqfrms/p/11886342.html

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function



©xgqfrms 2012-2020

www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2020-10-20 22:19  xgqfrms  阅读(487)  评论(9编辑  收藏  举报