index.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. 'use strict';
  2. const is = require('is-type-of');
  3. const IS_READY = Symbol('isReady');
  4. const READY_CALLBACKS = Symbol('readyCallbacks');
  5. const READY_ARG = Symbol('readyArg');
  6. class Ready {
  7. constructor() {
  8. this[IS_READY] = false;
  9. this[READY_CALLBACKS] = [];
  10. }
  11. ready(flagOrFunction) {
  12. // register a callback
  13. if (flagOrFunction === undefined || is.function(flagOrFunction)) {
  14. return this.register(flagOrFunction);
  15. }
  16. // emit callbacks
  17. this.emit(flagOrFunction);
  18. }
  19. /**
  20. * Register a callback to the callback stack, it will be called when emit.
  21. * It will return promise when no argument passing.
  22. * @param {Function|Undefined} func - a callback
  23. * @return {Undefined|Promise} promise
  24. */
  25. register(func) {
  26. // support `this.ready().then(onready);` and `yield this.ready()`;
  27. if (!func) {
  28. return new Promise((resolve, reject) => {
  29. function func(err) {
  30. if (err) {
  31. reject(err);
  32. } else {
  33. resolve();
  34. }
  35. }
  36. if (this[IS_READY]) {
  37. return func(this[READY_ARG]);
  38. }
  39. this[READY_CALLBACKS].push(func);
  40. });
  41. }
  42. // this.ready(fn)
  43. if (this[IS_READY]) {
  44. func(this[READY_ARG]);
  45. } else {
  46. this[READY_CALLBACKS].push(func);
  47. }
  48. }
  49. /**
  50. * Call the callbacks that has been registerd, and clean the callback stack.
  51. * If the flag is not false, it will be marked as ready. Then the callbacks will be called immediatly when register.
  52. * @param {Boolean|Error} flag - Set a flag whether it had been ready. If the flag is an error, it's also ready, but the callback will be called with argument `error`
  53. */
  54. emit(flag) {
  55. // this.ready(true);
  56. // this.ready(false);
  57. // this.ready(err);
  58. this[IS_READY] = flag !== false;
  59. this[READY_ARG] = flag instanceof Error ? flag : undefined;
  60. // this.ready(true)
  61. if (this[IS_READY]) {
  62. this[READY_CALLBACKS]
  63. .splice(0, Infinity)
  64. .forEach(callback => process.nextTick(() => callback(this[READY_ARG])));
  65. }
  66. }
  67. /**
  68. * @param {Object} obj - an object that be mixed
  69. */
  70. static mixin(obj) {
  71. if (!obj) return;
  72. const ready = new Ready();
  73. // delegate method
  74. obj.ready = flagOrFunction => ready.ready(flagOrFunction);
  75. }
  76. }
  77. function mixin(object) {
  78. Ready.mixin(object);
  79. }
  80. module.exports = mixin;
  81. module.exports.mixin = mixin;
  82. module.exports.Ready = Ready;