debounce-promise
Advanced tools
Comparing version 2.1.1 to 3.0.0
'use strict'; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
/* global setTimeout, clearTimeout */ | ||
module.exports = function debounce(fn) { | ||
var wait = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var _ref$leading = _ref.leading; | ||
var leading = _ref$leading === undefined ? false : _ref$leading; | ||
var _ref$accumulate = _ref.accumulate; | ||
var accumulate = _ref$accumulate === undefined ? false : _ref$accumulate; | ||
var nextArgs = []; | ||
var pending = void 0; | ||
var lastCallAt = void 0; | ||
var deferred = void 0; | ||
var timer = void 0; | ||
var pendingArgs = []; | ||
return function debounced() { | ||
var nextIdx = nextArgs.length; | ||
var currentWait = getWait(wait); | ||
var currentTime = new Date().getTime(); | ||
var isCold = !lastCallAt || currentTime - lastCallAt > currentWait; | ||
lastCallAt = currentTime; | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -24,56 +27,59 @@ args[_key] = arguments[_key]; | ||
nextArgs[nextIdx] = args; | ||
var onTimeout = void 0; | ||
var wasLeading = false; | ||
if (pending) { | ||
onTimeout = callOriginal.bind(this, nextArgs, pending); | ||
if (isCold && options.leading) { | ||
return options.accumulate ? fn.call(this, [args]).then(function (result) { | ||
return result[0]; | ||
}) : fn.call.apply(fn, [this].concat(args)); | ||
} | ||
if (deferred) { | ||
clearTimeout(timer); | ||
} else { | ||
pending = defer(); | ||
onTimeout = callOriginal.bind(this, nextArgs, pending); | ||
if (leading) { | ||
onTimeout(); | ||
nextArgs = []; | ||
onTimeout = clear; | ||
wasLeading = true; | ||
} | ||
deferred = defer(); | ||
} | ||
pendingArgs.push(args); | ||
timer = setTimeout(flush.bind(this), currentWait); | ||
if (options.accumulate) { | ||
var _ret = function () { | ||
var argsIndex = pendingArgs.length - 1; | ||
return { | ||
v: deferred.promise.then(function (results) { | ||
return results[argsIndex]; | ||
}) | ||
}; | ||
}(); | ||
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; | ||
} | ||
return deferred.promise; | ||
}; | ||
function flush() { | ||
var thisDeferred = deferred; | ||
clearTimeout(timer); | ||
timer = setTimeout(onTimeout, getWait(wait)); | ||
if (accumulate) { | ||
var _pending = pending; | ||
if (wasLeading) { | ||
pending = defer(); | ||
} | ||
return _pending.promise.then(function (res) { | ||
return res[nextIdx]; | ||
if (options.accumulate) { | ||
fn.call(this, pendingArgs).then(function (res) { | ||
return thisDeferred.resolve(res); | ||
}, function (err) { | ||
return thisDeferred.reject(err); | ||
}); | ||
} else { | ||
fn.apply(this, pendingArgs[pendingArgs.length - 1]).then(function (res) { | ||
return thisDeferred.resolve(res); | ||
}, function (err) { | ||
return thisDeferred.reject(err); | ||
}); | ||
} | ||
return pending.promise; | ||
}; | ||
function callOriginal(args, deferred) { | ||
var returnValue = accumulate ? fn.call(this, args) : fn.apply(this, args[args.length - 1]); | ||
returnValue.then(function (v) { | ||
deferred.resolve(v); | ||
clear(); | ||
}, function (err) { | ||
deferred.reject(err); | ||
clear(); | ||
}); | ||
pendingArgs = []; | ||
deferred = null; | ||
} | ||
}; | ||
function clear() { | ||
nextArgs = []; | ||
pending = null; | ||
timer = null; | ||
} | ||
function getWait(wait) { | ||
return typeof wait === 'function' ? wait() : wait; | ||
} | ||
function getWait(_wait) { | ||
if (typeof _wait === 'function') { | ||
return _wait(); | ||
} | ||
return _wait; | ||
} | ||
}; | ||
function defer() { | ||
@@ -80,0 +86,0 @@ var deferred = {}; |
89
index.js
/* global setTimeout, clearTimeout */ | ||
module.exports = function debounce (fn, wait = 0, {leading = false, accumulate = false} = {}) { | ||
let nextArgs = [] | ||
let pending | ||
module.exports = function debounce (fn, wait = 0, options = {}) { | ||
let lastCallAt | ||
let deferred | ||
let timer | ||
let pendingArgs = [] | ||
return function debounced (...args) { | ||
const nextIdx = nextArgs.length | ||
nextArgs[nextIdx] = args | ||
let onTimeout | ||
let wasLeading = false | ||
if (pending) { | ||
onTimeout = callOriginal.bind(this, nextArgs, pending) | ||
const currentWait = getWait(wait) | ||
const currentTime = new Date().getTime() | ||
const isCold = !lastCallAt || (currentTime - lastCallAt) > currentWait | ||
lastCallAt = currentTime | ||
if (isCold && options.leading) { | ||
return options.accumulate ? fn.call(this, [args]).then(result => result[0]) : fn.call(this, ...args) | ||
} | ||
if (deferred) { | ||
clearTimeout(timer) | ||
} else { | ||
pending = defer() | ||
onTimeout = callOriginal.bind(this, nextArgs, pending) | ||
if (leading) { | ||
onTimeout() | ||
nextArgs = [] | ||
onTimeout = clear | ||
wasLeading = true | ||
} | ||
deferred = defer() | ||
} | ||
clearTimeout(timer) | ||
timer = setTimeout(onTimeout, getWait(wait)) | ||
if (accumulate) { | ||
const _pending = pending | ||
if (wasLeading) { | ||
pending = defer() | ||
} | ||
return _pending.promise.then(res => res[nextIdx]) | ||
pendingArgs.push(args) | ||
timer = setTimeout(flush.bind(this), currentWait) | ||
if (options.accumulate) { | ||
const argsIndex = pendingArgs.length - 1 | ||
return deferred.promise.then(results => results[argsIndex]) | ||
} | ||
return pending.promise | ||
} | ||
function callOriginal (args, deferred) { | ||
const returnValue = accumulate ? fn.call(this, args) : fn.apply(this, args[args.length - 1]) | ||
returnValue.then(v => { | ||
deferred.resolve(v) | ||
clear() | ||
}, err => { | ||
deferred.reject(err) | ||
clear() | ||
}) | ||
return deferred.promise | ||
} | ||
function clear () { | ||
nextArgs = [] | ||
pending = null | ||
timer = null | ||
} | ||
function flush () { | ||
const thisDeferred = deferred | ||
clearTimeout(timer) | ||
if (options.accumulate) { | ||
fn.call(this, pendingArgs) | ||
.then(res => thisDeferred.resolve(res), err => thisDeferred.reject(err)) | ||
} else { | ||
fn.apply(this, pendingArgs[pendingArgs.length - 1]) | ||
.then(res => thisDeferred.resolve(res), err => thisDeferred.reject(err)) | ||
} | ||
function getWait (_wait) { | ||
if (typeof _wait === 'function') { | ||
return _wait() | ||
} | ||
return _wait | ||
pendingArgs = [] | ||
deferred = null | ||
} | ||
} | ||
function getWait (wait) { | ||
return (typeof wait === 'function') ? wait() : wait | ||
} | ||
function defer () { | ||
@@ -61,0 +58,0 @@ const deferred = {} |
{ | ||
"name": "debounce-promise", | ||
"version": "2.1.1", | ||
"version": "3.0.0", | ||
"description": "Create a debounced version of a promise returning function", | ||
@@ -5,0 +5,0 @@ "main": "dist/index", |
@@ -92,3 +92,3 @@ # debounce-promise | ||
Set `leading: true` if you | ||
want to call `func` immediately and use the value from the first call for all subsequent promises. | ||
want to call `func` and return its promise immediately. | ||
@@ -98,1 +98,28 @@ Set `accumulate: true` if you want the debounced function to be called with an array of all the arguments received while waiting. | ||
Supports passing a function as the `wait` parameter, which provides a way to lazily or dynamically define a wait timeout. | ||
## Example timeline illustration | ||
```js | ||
function refresh() { | ||
return fetch('/my/api/something') | ||
} | ||
const debounced = debounce(refresh, 100) | ||
``` | ||
``` | ||
time (ms) -> 0 --- 10 --- 50 --- 100 --- | ||
----------------------------------------------- | ||
debounced() | --- P(1) --- P(1) --- P(1) --- | ||
refresh() | --------------------- P(1) --- | ||
``` | ||
```js | ||
const debounced = debounce(refresh, 100, {leading: true}) | ||
``` | ||
``` | ||
time (ms) -> 0 --- 10 --- 50 --- 100 --- 110 --- | ||
-------------------------------------------------------- | ||
debounced() | --- P(1) --- P(2) --- P(2) --- P(2) --- | ||
refresh() | --- P(1) --------------------- P(2) --- | ||
``` |
@@ -30,3 +30,3 @@ /* global setTimeout */ | ||
t.deepEqual(results, ['foo', 'foo', 'foo', 'foo']) | ||
t.deepEqual(results, ['foo', 'qux', 'qux', 'qux']) | ||
}) | ||
@@ -70,8 +70,14 @@ | ||
let callCount = 0 | ||
const debounced = debounce(async () => callCount++, () => 10) | ||
let getWaitCallCount = 0 | ||
const debounced = debounce(async () => callCount++, () => { | ||
getWaitCallCount++ | ||
return 100 | ||
}) | ||
debounced() | ||
debounced() | ||
debounced() | ||
await sleep(90) | ||
t.equal(callCount, 0) | ||
await sleep(20) | ||
t.inequal(getWaitCallCount, 0) | ||
t.equal(callCount, 1) | ||
@@ -121,3 +127,3 @@ }) | ||
test('calls debounced function with accumulates arguments', async t => { | ||
test('calls debounced function and accumulates arguments', async t => { | ||
function squareBatch (args) { | ||
@@ -124,0 +130,0 @@ t.deepEqual(args, [[1], [2], [3]]) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16970
124
262