const { debounce } = require('lodash');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
const debounced = debounce(logger, 2000);
setTimeout(() => {
debounced();
}, 1000);
setTimeout(() => {
debounced();
}, 2000);
setTimeout(() => {
debounced();
}, 3000);
setTimeout(() => {
debounced();
}, 4000);
setTimeout(() => {
debounced();
}, 5000);
const { throttle } = require('lodash');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
const throttled = throttle(logger, 2990);
setTimeout(() => {
throttled();
}, 1000);
setTimeout(() => {
throttled();
}, 2000);
setTimeout(() => {
throttled();
}, 3000);
setTimeout(() => {
throttled();
}, 4000);
setTimeout(() => {
throttled();
}, 5000);
setTimeout(() => {
throttled();
}, 6000);
let debounce = require('./debounce');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
let debounced = debounce(logger, 2000);
setTimeout(() => {
debounced();
}, 1000);
setTimeout(() => {
debounced();
}, 2000);
setTimeout(() => {
debounced();
}, 3000);
setTimeout(() => {
debounced();
}, 4000);
setTimeout(() => {
debounced();
}, 5000);
function debounce(fn, wait) {
let timer;
const debouncedFn = function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(fn, wait);
}
return debouncedFn;
}
module.exports = debounce;
let debounce = require('./debounce');
const start = Date.now();
+function logger(age) {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
+ console.log(this, age);
}
let debounced = debounce(logger, 2000);
+let obj = {
+ name: '张三',
+ debounced
+}
setTimeout(() => {
+ obj.debounced(1000);
}, 1000);
setTimeout(() => {
+ obj.debounced(2000);
}, 2000);
setTimeout(() => {
+ obj.debounced(3000);
}, 3000);
setTimeout(() => {
+ obj.debounced(4000);
}, 4000);
setTimeout(() => {
+ obj.debounced(5000);
}, 5000);
function debounce(fn, wait) {
let timer;
const debouncedFn = function (...args) {
if (timer) {
clearTimeout(timer);
timer = null;
}
+ timer = setTimeout(() => {
+ fn.apply(this, args);
+ }, wait);
}
return debouncedFn;
}
module.exports = debounce;
+function debounce(fn, wait, immediate = false) {
let timer;
+ let immediateInvoked = false;
const debouncedFn = function (...args) {
if (timer) {
clearTimeout(timer);
timer = null;
}
+ if (immediate && !immediateInvoked) {
+ fn.apply(this, args);
+ immediateInvoked = true;
+ } else {
timer = setTimeout(() => {
fn.apply(this, args);
+ immediateInvoked = false;
}, wait);
}
}
return debouncedFn;
}
module.exports = debounce;
let debounce = require('./debounce');
const start = Date.now();
function logger(age) {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
console.log(this, age);
}
let debounced = debounce(logger, 2000, true);
let obj = {
name: '张三',
debounced
}
setTimeout(() => {
obj.debounced(1000);
}, 1000);
setTimeout(() => {
obj.debounced(2000);
}, 2000);
setTimeout(() => {
obj.debounced(3000);
}, 3000);
setTimeout(() => {
obj.debounced(4000);
}, 4000);
setTimeout(() => {
obj.debounced(5000);
+ obj.debounced.cancel();
}, 5000);
debounce.js
function debounce(fn, wait, immediate = false) {
let timer;
let immediateInvoked = false;
const debouncedFn = function (...args) {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (immediate && !immediateInvoked) {
fn.apply(this, args);
immediateInvoked = true;
} else {
timer = setTimeout(() => {
fn.apply(this, args);
immediateInvoked = false;
}, wait);
}
}
+ debouncedFn.cancel = function () {
+ if (timer) {
+ clearTimeout(timer);
+ timer = null;
+ immediateInvoked = false;
+ }
+ }
return debouncedFn;
}
module.exports = debounce;
let debounce = require('./debounce');
const start = Date.now();
function logger(age) {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
console.log(this, age);
+ return Date.now();
}
+let debounced = debounce(logger, 2000, true, (err, data) => {
+ console.log('callback', data);
+});
let obj = {
name: '张三',
debounced
}
setTimeout(() => {
obj.debounced(1000);
}, 1000);
setTimeout(() => {
obj.debounced(2000);
}, 2000);
setTimeout(() => {
obj.debounced(3000);
}, 3000);
setTimeout(() => {
obj.debounced(4000);
}, 4000);
+setTimeout(() => {
+ obj.debounced(5000).then((data) => {
+ console.log('then', data);
+ });
+ //obj.debounced.cancel();
+}, 5000);
+function debounce(fn, wait, immediate = false, callback = () => { }) {
+ if (typeof immediate === 'function') {
+ callback = immediate;
+ immediate = false;
+ }
let timer;
let immediateInvoked = false;
const debouncedFn = function (...args) {
+ return new Promise((resolve, reject) => {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (immediate && !immediateInvoked) {
try {
+ const result = fn.apply(this, args);
+ callback(null, result);
+ resolve(result);
+ } catch (error) {
+ callback(error);
+ reject(error);
+ } finally {
+ immediateInvoked = true;
+ }
} else {
timer = setTimeout(() => {
try {
+ const result = fn.apply(this, args);
+ callback(null, result);
+ resolve(result);
+ } catch (error) {
+ callback(error);
+ reject(error);
+ } finally {
+ immediateInvoked = false;
+ }
}, wait);
}
+ });
}
debouncedFn.cancel = function () {
if (timer) {
clearTimeout(timer);
timer = null;
immediateInvoked = false;
}
}
return debouncedFn;
}
module.exports = debounce;
//let { throttle } = require('lodash');
const throttle = require('./throttle');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
let throttled = throttle(logger, 2990);
setTimeout(() => {
throttled();
}, 1000);
setTimeout(() => {
throttled();
}, 2000);
setTimeout(() => {
throttled();
}, 3000);
setTimeout(() => {
throttled();
}, 4000);
setTimeout(() => {
throttled();
}, 5000);
setTimeout(() => {
throttled();
}, 6000);
function throttle(fn, wait) {
let lastExecTime = 0;
const throttledFn = function (...args) {
const currentTime = Date.now();
const nextExecTime = lastExecTime + wait;
if (currentTime >= nextExecTime) {
fn.apply(this, args);
lastExecTime = currentTime;
}
}
return throttledFn;
}
module.exports = throttle;
//let { throttle } = require('lodash');
const throttle = require('./throttle');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
+let throttled = throttle(logger, 2990, false);
setTimeout(() => {
throttled();
}, 1000);
setTimeout(() => {
throttled();
}, 2000);
setTimeout(() => {
throttled();
}, 3000);
setTimeout(() => {
throttled();
}, 4000);
setTimeout(() => {
throttled();
}, 5000);
setTimeout(() => {
throttled();
}, 6000);
function throttle(fn, wait, options = { leading: true }) {
+ const { leading } = options;
let lastExecTime = 0;
const throttledFn = function (...args) {
const currentTime = Date.now();
+ if (lastExecTime === 0 && !leading) {
+ lastExecTime = currentTime;
+ }
const nextExecTime = lastExecTime + wait;
if (currentTime >= nextExecTime) {
fn.apply(this, args);
lastExecTime = currentTime;
}
}
return throttledFn;
}
module.exports = throttle;
//let { throttle } = require('lodash');
const throttle = require('./throttle');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
}
+let throttled = throttle(logger, 2990, { leading: false, trailing: false });
setTimeout(() => {
throttled();
}, 1000);
setTimeout(() => {
throttled();
}, 2000);
setTimeout(() => {
throttled();
}, 3000);
setTimeout(() => {
throttled();
}, 4000);
setTimeout(() => {
throttled();
}, 5000);
setTimeout(() => {
throttled();
}, 6000);
+function throttle(fn, wait, options = { leading: true, trailing: true }) {
+ const { leading, trailing } = options;
let lastExecTime = 0;
+ let timer;
const throttledFn = function (...args) {
const currentTime = Date.now();
if (lastExecTime === 0 && !leading) {
lastExecTime = currentTime;
}
const nextExecTime = lastExecTime + wait;
+ if (currentTime >= nextExecTime) {
+ if (timer) { clearTimeout(timer); timer = null; }
fn.apply(this, args);
lastExecTime = currentTime;
+ } else {
+ if (trailing) {
+ if (timer) { clearTimeout(timer); timer = null; }
+ timer = setTimeout(() => {
+ fn.apply(this, args);
+ lastExecTime = Date.now();
+ }, (nextExecTime - currentTime));
+ }
+ }
}
return throttledFn;
}
module.exports = throttle;
//let { throttle } = require('lodash');
const throttle = require('./throttle');
const start = Date.now();
function logger() {
console.log((Math.floor((Date.now() - start) / 1000)) + 's');
+ return Date.now();
}
let throttled = throttle(logger, 2990, {
leading: true,
trailing: true,
+ callback: (err, data) => {
+ console.log('callback', data);
+ }
});
setTimeout(() => {
throttled();
}, 1000);
setTimeout(() => {
throttled();
}, 2000);
setTimeout(() => {
throttled();
}, 3000);
setTimeout(() => {
throttled();
}, 4000);
setTimeout(() => {
throttled();
}, 5000);
setTimeout(() => {
+ throttled().then(data => {
+ console.log('promise', data);
+ });
+ //throttled.cancel();
}, 6000);
+function throttle(fn, wait, options = { leading: true, trailing: true, callback: () => { } }) {
+ const { leading, trailing, callback } = options;
let lastExecTime = 0;
let timer;
+ const throttledFn = function (...args) {
+ return new Promise((resolve, reject) => {
const currentTime = Date.now();
if (lastExecTime === 0 && !leading) {
lastExecTime = currentTime;
}
const nextExecTime = lastExecTime + wait;
if (currentTime >= nextExecTime) {
if (timer) { clearTimeout(timer); timer = null; }
+ try {
+ const result = fn.apply(this, args);
+ callback(null, result);
+ resolve(result);
+ } catch (error) {
+ callback(error);
+ reject(error)
+ }
lastExecTime = currentTime;
} else {
if (trailing) {
if (timer) { clearTimeout(timer); timer = null; }
timer = setTimeout(() => {
+ try {
+ const result = fn.apply(this, args);
+ callback(null, result);
+ resolve(result);
+ } catch (error) {
+ callback(error);
+ reject(error)
+ }
lastExecTime = Date.now();
}, (nextExecTime - currentTime));
}
}
+ });
}
throttledFn.cancel = function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
}
return throttledFn;
}
module.exports = throttle;
// Is a given variable an object?
export default function isObject(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
}
import isObject from './isObject.js'
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked, or until the next browser frame is drawn. The debounced function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* debounced function. Subsequent calls to the debounced function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `debounce` and `throttle`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0]
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
* used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', debounce(calculateLayout, 150))
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }))
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
* const source = new EventSource('/stream')
* jQuery(source).on('message', debounced)
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel)
*
* // Check for pending invocations.
* const status = debounced.pending() ? "Pending..." : "Ready"
*/
function debounce(func, wait, options) {
let lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime
let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = (!wait && wait !== 0 && typeof requestAnimationFrame === 'function')
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}
function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
function startTimer(pendingFunc, wait) {
if (useRAF) {
cancelAnimationFrame(timerId)
return requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}
function cancelTimer(id) {
if (useRAF) {
return cancelAnimationFrame(id)
}
clearTimeout(id)
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time
// Start the timer for the trailing edge.
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}
function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall
return maxing
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting
}
function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
}
function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
// Restart the timer.
timerId = startTimer(timerExpired, remainingWait(time))
}
function trailingEdge(time) {
timerId = undefined
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}
function pending() {
return timerId !== undefined
}
function debounced(...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}
export default debounce
import debounce from './debounce.js'
import isObject from './isObject.js'
/**
* Creates a throttled function that only invokes `func` at most once per
* every `wait` milliseconds (or once per browser frame). The throttled function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* throttled function. Subsequent calls to the throttled function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the throttled function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `throttle` and `debounce`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to throttle.
* @param {number} [wait=0]
* The number of milliseconds to throttle invocations to; if omitted,
* `requestAnimationFrame` is used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=true]
* Specify invoking on the leading edge of the timeout.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new throttled function.
* @example
*
* // Avoid excessively updating the position while scrolling.
* jQuery(window).on('scroll', throttle(updatePosition, 100))
*
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
* const throttled = throttle(renewToken, 300000, { 'trailing': false })
* jQuery(element).on('click', throttled)
*
* // Cancel the trailing throttled invocation.
* jQuery(window).on('popstate', throttled.cancel)
*/
function throttle(func, wait, options) {
let leading = true
let trailing = true
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading
trailing = 'trailing' in options ? !!options.trailing : trailing
}
return debounce(func, wait, {
leading,
trailing,
'maxWait': wait
})
}
export default throttle