dispatcher-base.js 4.5 KB

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