dispatcher-base.js 3.5 KB

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