dispatcher-base.js 4.4 KB

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