dispatcher-base.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. 'use strict'
  2. const Dispatcher = require('./dispatcher')
  3. const UnwrapHandler = require('../handler/unwrap-handler')
  4. const {
  5. ClientDestroyedError,
  6. ClientClosedError,
  7. InvalidArgumentError
  8. } = require('../core/errors')
  9. const { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/symbols')
  10. const kOnDestroyed = Symbol('onDestroyed')
  11. const kOnClosed = Symbol('onClosed')
  12. class DispatcherBase extends Dispatcher {
  13. /** @type {boolean} */
  14. [kDestroyed] = false;
  15. /** @type {Array<Function|null} */
  16. [kOnDestroyed] = null;
  17. /** @type {boolean} */
  18. [kClosed] = false;
  19. /** @type {Array<Function>|null} */
  20. [kOnClosed] = null
  21. /** @returns {boolean} */
  22. get destroyed () {
  23. return this[kDestroyed]
  24. }
  25. /** @returns {boolean} */
  26. get closed () {
  27. return this[kClosed]
  28. }
  29. close (callback) {
  30. if (callback === undefined) {
  31. return new Promise((resolve, reject) => {
  32. this.close((err, data) => {
  33. return err ? reject(err) : resolve(data)
  34. })
  35. })
  36. }
  37. if (typeof callback !== 'function') {
  38. throw new InvalidArgumentError('invalid callback')
  39. }
  40. if (this[kDestroyed]) {
  41. const err = new ClientDestroyedError()
  42. queueMicrotask(() => callback(err, null))
  43. return
  44. }
  45. if (this[kClosed]) {
  46. if (this[kOnClosed]) {
  47. this[kOnClosed].push(callback)
  48. } else {
  49. queueMicrotask(() => callback(null, null))
  50. }
  51. return
  52. }
  53. this[kClosed] = true
  54. this[kOnClosed] ??= []
  55. this[kOnClosed].push(callback)
  56. const onClosed = () => {
  57. const callbacks = this[kOnClosed]
  58. this[kOnClosed] = null
  59. for (let i = 0; i < callbacks.length; i++) {
  60. callbacks[i](null, null)
  61. }
  62. }
  63. // Should not error.
  64. this[kClose]()
  65. .then(() => this.destroy())
  66. .then(() => queueMicrotask(onClosed))
  67. }
  68. destroy (err, callback) {
  69. if (typeof err === 'function') {
  70. callback = err
  71. err = null
  72. }
  73. if (callback === undefined) {
  74. return new Promise((resolve, reject) => {
  75. this.destroy(err, (err, data) => {
  76. return err ? reject(err) : resolve(data)
  77. })
  78. })
  79. }
  80. if (typeof callback !== 'function') {
  81. throw new InvalidArgumentError('invalid callback')
  82. }
  83. if (this[kDestroyed]) {
  84. if (this[kOnDestroyed]) {
  85. this[kOnDestroyed].push(callback)
  86. } else {
  87. queueMicrotask(() => callback(null, null))
  88. }
  89. return
  90. }
  91. if (!err) {
  92. err = new ClientDestroyedError()
  93. }
  94. this[kDestroyed] = true
  95. this[kOnDestroyed] ??= []
  96. this[kOnDestroyed].push(callback)
  97. const onDestroyed = () => {
  98. const callbacks = this[kOnDestroyed]
  99. this[kOnDestroyed] = null
  100. for (let i = 0; i < callbacks.length; i++) {
  101. callbacks[i](null, null)
  102. }
  103. }
  104. // Should not error.
  105. this[kDestroy](err)
  106. .then(() => queueMicrotask(onDestroyed))
  107. }
  108. dispatch (opts, handler) {
  109. if (!handler || typeof handler !== 'object') {
  110. throw new InvalidArgumentError('handler must be an object')
  111. }
  112. handler = UnwrapHandler.unwrap(handler)
  113. try {
  114. if (!opts || typeof opts !== 'object') {
  115. throw new InvalidArgumentError('opts must be an object.')
  116. }
  117. if (this[kDestroyed] || this[kOnDestroyed]) {
  118. throw new ClientDestroyedError()
  119. }
  120. if (this[kClosed]) {
  121. throw new ClientClosedError()
  122. }
  123. return this[kDispatch](opts, handler)
  124. } catch (err) {
  125. if (typeof handler.onError !== 'function') {
  126. throw err
  127. }
  128. handler.onError(err)
  129. return false
  130. }
  131. }
  132. }
  133. module.exports = DispatcherBase