f85ddcec5a0a7924aee10a6fb21b5f2ad3a48486790a8f3582d34233fa52164f534791840ff26251286989ecae08b571a2e8db89941083a6e86db9fcf772ec 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import {arrayReduce} from './array';
  2. /**
  3. * Checks if given variable is function.
  4. *
  5. * @param {*} func Variable to check.
  6. * @returns {Boolean}
  7. */
  8. export function isFunction(func) {
  9. return typeof func === 'function';
  10. }
  11. /**
  12. * Creates throttle function that enforces a maximum number of times a function (`func`) can be called over time (`wait`).
  13. *
  14. * @param {Function} func Function to invoke.
  15. * @param {Number} wait Delay in miliseconds.
  16. * @returns {Function}
  17. */
  18. export function throttle(func, wait = 200) {
  19. let lastCalled = 0;
  20. let result = {
  21. lastCallThrottled: true
  22. };
  23. let lastTimer = null;
  24. function _throttle() {
  25. const args = arguments;
  26. let stamp = Date.now();
  27. let needCall = false;
  28. result.lastCallThrottled = true;
  29. if (!lastCalled) {
  30. lastCalled = stamp;
  31. needCall = true;
  32. }
  33. let remaining = wait - (stamp - lastCalled);
  34. if (needCall) {
  35. result.lastCallThrottled = false;
  36. func.apply(this, args);
  37. } else {
  38. if (lastTimer) {
  39. clearTimeout(lastTimer);
  40. }
  41. lastTimer = setTimeout(() => {
  42. result.lastCallThrottled = false;
  43. func.apply(this, args);
  44. lastCalled = 0;
  45. lastTimer = void 0;
  46. }, remaining);
  47. }
  48. return result;
  49. }
  50. return _throttle;
  51. }
  52. /**
  53. * Creates throttle function that enforces a maximum number of times a function (`func`) can be called over
  54. * time (`wait`) after specified hits.
  55. *
  56. * @param {Function} func Function to invoke.
  57. * @param {Number} wait Delay in miliseconds.
  58. * @param {Number} hits Number of hits after throttling will be applied.
  59. * @returns {Function}
  60. */
  61. export function throttleAfterHits(func, wait = 200, hits = 10) {
  62. const funcThrottle = throttle(func, wait);
  63. let remainHits = hits;
  64. function _clearHits() {
  65. remainHits = hits;
  66. }
  67. function _throttleAfterHits() {
  68. if (remainHits) {
  69. remainHits--;
  70. return func.apply(this, arguments);
  71. }
  72. return funcThrottle.apply(this, arguments);
  73. }
  74. _throttleAfterHits.clearHits = _clearHits;
  75. return _throttleAfterHits;
  76. }
  77. /**
  78. * Creates debounce function that enforces a function (`func`) not be called again until a certain amount of time (`wait`)
  79. * has passed without it being called.
  80. *
  81. * @param {Function} func Function to invoke.
  82. * @param {Number} wait Delay in milliseconds.
  83. * @returns {Function}
  84. */
  85. export function debounce(func, wait = 200) {
  86. let lastTimer = null;
  87. let result;
  88. function _debounce() {
  89. const args = arguments;
  90. if (lastTimer) {
  91. clearTimeout(lastTimer);
  92. }
  93. lastTimer = setTimeout(() => {
  94. result = func.apply(this, args);
  95. }, wait);
  96. return result;
  97. }
  98. return _debounce;
  99. }
  100. /**
  101. * Creates the function that returns the result of calling the given functions. Result of the first function is passed to
  102. * the second as an argument and so on. Only first function in the chain can handle multiple arguments.
  103. *
  104. * @param {Function} functions Functions to compose.
  105. * @returns {Function}
  106. */
  107. export function pipe(...functions) {
  108. const [firstFunc, ...restFunc] = functions;
  109. return function _pipe() {
  110. return arrayReduce(restFunc, (acc, fn) => fn(acc), firstFunc.apply(this, arguments));
  111. };
  112. }
  113. /**
  114. * Creates the function that returns the function with cached arguments.
  115. *
  116. * @param {Function} func Function to partialization.
  117. * @param {Array} params Function arguments to cache.
  118. * @returns {Function}
  119. */
  120. export function partial(func, ...params) {
  121. return function _partial(...restParams) {
  122. return func.apply(this, params.concat(restParams));
  123. };
  124. }
  125. /**
  126. * Creates the functions that returns the function with cached arguments. If count if passed arguments will be matched
  127. * to the arguments defined in `func` then function will be invoked.
  128. * Arguments are added to the stack in direction from the left to the right.
  129. *
  130. * @example
  131. * ```
  132. * var replace = curry(function(find, replace, string) {
  133. * return string.replace(find, replace);
  134. * });
  135. *
  136. * // returns function with bounded first argument
  137. * var replace = replace('foo')
  138. *
  139. * // returns replaced string - all arguments was passed so function was invoked
  140. * replace('bar', 'Some test with foo...');
  141. *
  142. * ```
  143. *
  144. * @param {Function} func Function to currying.
  145. * @returns {Function}
  146. */
  147. export function curry(func) {
  148. const argsLength = func.length;
  149. function given(argsSoFar) {
  150. return function _curry(...params) {
  151. const passedArgsSoFar = argsSoFar.concat(params);
  152. let result;
  153. if (passedArgsSoFar.length >= argsLength) {
  154. result = func.apply(this, passedArgsSoFar);
  155. } else {
  156. result = given(passedArgsSoFar);
  157. }
  158. return result;
  159. };
  160. }
  161. return given([]);
  162. }
  163. /**
  164. * Creates the functions that returns the function with cached arguments. If count if passed arguments will be matched
  165. * to the arguments defined in `func` then function will be invoked.
  166. * Arguments are added to the stack in direction from the right to the left.
  167. *
  168. * @example
  169. * ```
  170. * var replace = curry(function(find, replace, string) {
  171. * return string.replace(find, replace);
  172. * });
  173. *
  174. * // returns function with bounded first argument
  175. * var replace = replace('Some test with foo...')
  176. *
  177. * // returns replaced string - all arguments was passed so function was invoked
  178. * replace('bar', 'foo');
  179. *
  180. * ```
  181. *
  182. * @param {Function} func Function to currying.
  183. * @returns {Function}
  184. */
  185. export function curryRight(func) {
  186. const argsLength = func.length;
  187. function given(argsSoFar) {
  188. return function _curry(...params) {
  189. const passedArgsSoFar = argsSoFar.concat(params.reverse());
  190. let result;
  191. if (passedArgsSoFar.length >= argsLength) {
  192. result = func.apply(this, passedArgsSoFar);
  193. } else {
  194. result = given(passedArgsSoFar);
  195. }
  196. return result;
  197. };
  198. }
  199. return given([]);
  200. }