Batchinator.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow strict-local
  9. */
  10. 'use strict';
  11. const InteractionManager = require('./InteractionManager');
  12. /**
  13. * A simple class for batching up invocations of a low-pri callback. A timeout is set to run the
  14. * callback once after a delay, no matter how many times it's scheduled. Once the delay is reached,
  15. * InteractionManager.runAfterInteractions is used to invoke the callback after any hi-pri
  16. * interactions are done running.
  17. *
  18. * Make sure to cleanup with dispose(). Example:
  19. *
  20. * class Widget extends React.Component {
  21. * _batchedSave: new Batchinator(() => this._saveState, 1000);
  22. * _saveSate() {
  23. * // save this.state to disk
  24. * }
  25. * componentDidUpdate() {
  26. * this._batchedSave.schedule();
  27. * }
  28. * componentWillUnmount() {
  29. * this._batchedSave.dispose();
  30. * }
  31. * ...
  32. * }
  33. */
  34. class Batchinator {
  35. _callback: () => void;
  36. _delay: number;
  37. _taskHandle: ?{cancel: () => void, ...};
  38. constructor(callback: () => void, delayMS: number) {
  39. this._delay = delayMS;
  40. this._callback = callback;
  41. }
  42. /*
  43. * Cleanup any pending tasks.
  44. *
  45. * By default, if there is a pending task the callback is run immediately. Set the option abort to
  46. * true to not call the callback if it was pending.
  47. */
  48. dispose(options: {abort: boolean, ...} = {abort: false}) {
  49. if (this._taskHandle) {
  50. this._taskHandle.cancel();
  51. if (!options.abort) {
  52. this._callback();
  53. }
  54. this._taskHandle = null;
  55. }
  56. }
  57. schedule() {
  58. if (this._taskHandle) {
  59. return;
  60. }
  61. const timeoutHandle = setTimeout(() => {
  62. this._taskHandle = InteractionManager.runAfterInteractions(() => {
  63. // Note that we clear the handle before invoking the callback so that if the callback calls
  64. // schedule again, it will actually schedule another task.
  65. this._taskHandle = null;
  66. this._callback();
  67. });
  68. }, this._delay);
  69. this._taskHandle = {cancel: () => clearTimeout(timeoutHandle)};
  70. }
  71. }
  72. module.exports = Batchinator;