23
23
24
24
const {
25
25
ArrayPrototypeJoin,
26
- ArrayPrototypeShift,
26
+ ArrayPrototypePop,
27
+ ArrayPrototypePush,
27
28
ArrayPrototypeSlice,
28
29
ArrayPrototypeSplice,
29
30
ArrayPrototypeUnshift,
@@ -33,6 +34,7 @@ const {
33
34
FunctionPrototypeBind,
34
35
FunctionPrototypeCall,
35
36
NumberIsNaN,
37
+ NumberMAX_SAFE_INTEGER,
36
38
ObjectCreate,
37
39
ObjectDefineProperty,
38
40
ObjectDefineProperties,
@@ -59,6 +61,8 @@ const {
59
61
} = require ( 'internal/util/inspect' ) ;
60
62
61
63
let spliceOne ;
64
+ let FixedQueue ;
65
+ let kFirstEventParam ;
62
66
63
67
const {
64
68
AbortError,
@@ -73,6 +77,7 @@ const {
73
77
} = require ( 'internal/errors' ) ;
74
78
75
79
const {
80
+ validateInteger,
76
81
validateAbortSignal,
77
82
validateBoolean,
78
83
validateFunction,
@@ -84,6 +89,7 @@ const kErrorMonitor = Symbol('events.errorMonitor');
84
89
const kMaxEventTargetListeners = Symbol ( 'events.maxEventTargetListeners' ) ;
85
90
const kMaxEventTargetListenersWarned =
86
91
Symbol ( 'events.maxEventTargetListenersWarned' ) ;
92
+ const kWatermarkData = SymbolFor ( 'nodejs.watermarkData' ) ;
87
93
88
94
let EventEmitterAsyncResource ;
89
95
// The EventEmitterAsyncResource has to be initialized lazily because event.js
@@ -999,25 +1005,44 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
999
1005
* Returns an `AsyncIterator` that iterates `event` events.
1000
1006
* @param {EventEmitter } emitter
1001
1007
* @param {string | symbol } event
1002
- * @param {{ signal: AbortSignal; } } [options]
1008
+ * @param {{
1009
+ * signal: AbortSignal;
1010
+ * close?: string[];
1011
+ * highWatermark?: number,
1012
+ * lowWatermark?: number
1013
+ * }} [options]
1003
1014
* @returns {AsyncIterator }
1004
1015
*/
1005
- function on ( emitter , event , options ) {
1006
- const signal = options ?. signal ;
1016
+ function on ( emitter , event , options = { } ) {
1017
+ // Parameters validation
1018
+ const signal = options . signal ;
1007
1019
validateAbortSignal ( signal , 'options.signal' ) ;
1008
1020
if ( signal ?. aborted )
1009
1021
throw new AbortError ( undefined , { cause : signal ?. reason } ) ;
1010
-
1011
- const unconsumedEvents = [ ] ;
1012
- const unconsumedPromises = [ ] ;
1022
+ const highWatermark = options . highWatermark ?? NumberMAX_SAFE_INTEGER ;
1023
+ validateInteger ( highWatermark , 'options.highWatermark' , 1 ) ;
1024
+ const lowWatermark = options . lowWatermark ?? 1 ;
1025
+ validateInteger ( lowWatermark , 'options.lowWatermark' , 1 ) ;
1026
+
1027
+ // Preparing controlling queues and variables
1028
+ FixedQueue ??= require ( 'internal/fixed_queue' ) ;
1029
+ const unconsumedEvents = new FixedQueue ( ) ;
1030
+ const unconsumedPromises = new FixedQueue ( ) ;
1031
+ let paused = false ;
1013
1032
let error = null ;
1014
1033
let finished = false ;
1034
+ let size = 0 ;
1015
1035
1016
1036
const iterator = ObjectSetPrototypeOf ( {
1017
1037
next ( ) {
1018
1038
// First, we consume all unread events
1019
- const value = unconsumedEvents . shift ( ) ;
1020
- if ( value ) {
1039
+ if ( size ) {
1040
+ const value = unconsumedEvents . shift ( ) ;
1041
+ size -- ;
1042
+ if ( paused && size < lowWatermark ) {
1043
+ emitter . resume ( ) ;
1044
+ paused = false ;
1045
+ }
1021
1046
return PromiseResolve ( createIterResult ( value , false ) ) ;
1022
1047
}
1023
1048
@@ -1032,9 +1057,7 @@ function on(emitter, event, options) {
1032
1057
}
1033
1058
1034
1059
// If the iterator is finished, resolve to done
1035
- if ( finished ) {
1036
- return PromiseResolve ( createIterResult ( undefined , true ) ) ;
1037
- }
1060
+ if ( finished ) return closeHandler ( ) ;
1038
1061
1039
1062
// Wait until an event happens
1040
1063
return new Promise ( function ( resolve , reject ) {
@@ -1043,46 +1066,62 @@ function on(emitter, event, options) {
1043
1066
} ,
1044
1067
1045
1068
return ( ) {
1046
- eventTargetAgnosticRemoveListener ( emitter , event , eventHandler ) ;
1047
- eventTargetAgnosticRemoveListener ( emitter , 'error' , errorHandler ) ;
1048
-
1049
- if ( signal ) {
1050
- eventTargetAgnosticRemoveListener (
1051
- signal ,
1052
- 'abort' ,
1053
- abortListener ,
1054
- { once : true } ) ;
1055
- }
1056
-
1057
- finished = true ;
1058
-
1059
- for ( const promise of unconsumedPromises ) {
1060
- promise . resolve ( createIterResult ( undefined , true ) ) ;
1061
- }
1062
-
1063
- return PromiseResolve ( createIterResult ( undefined , true ) ) ;
1069
+ return closeHandler ( ) ;
1064
1070
} ,
1065
1071
1066
1072
throw ( err ) {
1067
1073
if ( ! err || ! ( err instanceof Error ) ) {
1068
1074
throw new ERR_INVALID_ARG_TYPE ( 'EventEmitter.AsyncIterator' ,
1069
1075
'Error' , err ) ;
1070
1076
}
1071
- error = err ;
1072
- eventTargetAgnosticRemoveListener ( emitter , event , eventHandler ) ;
1073
- eventTargetAgnosticRemoveListener ( emitter , 'error' , errorHandler ) ;
1077
+ errorHandler ( err ) ;
1074
1078
} ,
1075
-
1076
1079
[ SymbolAsyncIterator ] ( ) {
1077
1080
return this ;
1078
- }
1081
+ } ,
1082
+ [ kWatermarkData ] : {
1083
+ /**
1084
+ * The current queue size
1085
+ */
1086
+ get size ( ) {
1087
+ return size ;
1088
+ } ,
1089
+ /**
1090
+ * The low watermark. The emitter is resumed every time size is lower than it
1091
+ */
1092
+ get low ( ) {
1093
+ return lowWatermark ;
1094
+ } ,
1095
+ /**
1096
+ * The high watermark. The emitter is paused every time size is higher than it
1097
+ */
1098
+ get high ( ) {
1099
+ return highWatermark ;
1100
+ } ,
1101
+ /**
1102
+ * It checks wether the emitter is paused by the watermark controller or not
1103
+ */
1104
+ get isPaused ( ) {
1105
+ return paused ;
1106
+ }
1107
+ } ,
1079
1108
} , AsyncIteratorPrototype ) ;
1080
1109
1081
- eventTargetAgnosticAddListener ( emitter , event , eventHandler ) ;
1110
+ // Adding event handlers
1111
+ const { addEventListener, removeAll } = listenersController ( ) ;
1112
+ kFirstEventParam ??= require ( 'internal/events/symbols' ) . kFirstEventParam ;
1113
+ addEventListener ( emitter , event , options [ kFirstEventParam ] ? eventHandler : function ( ...args ) {
1114
+ return eventHandler ( args ) ;
1115
+ } ) ;
1082
1116
if ( event !== 'error' && typeof emitter . on === 'function' ) {
1083
- emitter . on ( 'error' , errorHandler ) ;
1117
+ addEventListener ( emitter , 'error' , errorHandler ) ;
1118
+ }
1119
+ const closeEvents = options ?. close ;
1120
+ if ( closeEvents ?. length ) {
1121
+ for ( let i = 0 ; i < closeEvents . length ; i ++ ) {
1122
+ addEventListener ( emitter , closeEvents [ i ] , closeHandler ) ;
1123
+ }
1084
1124
}
1085
-
1086
1125
if ( signal ) {
1087
1126
eventTargetAgnosticAddListener (
1088
1127
signal ,
@@ -1097,27 +1136,48 @@ function on(emitter, event, options) {
1097
1136
errorHandler ( new AbortError ( undefined , { cause : signal ?. reason } ) ) ;
1098
1137
}
1099
1138
1100
- function eventHandler ( ...args ) {
1101
- const promise = ArrayPrototypeShift ( unconsumedPromises ) ;
1102
- if ( promise ) {
1103
- promise . resolve ( createIterResult ( args , false ) ) ;
1104
- } else {
1105
- unconsumedEvents . push ( args ) ;
1106
- }
1139
+ function eventHandler ( value ) {
1140
+ if ( unconsumedPromises . isEmpty ( ) ) {
1141
+ size ++ ;
1142
+ if ( ! paused && size > highWatermark ) {
1143
+ paused = true ;
1144
+ emitter . pause ( ) ;
1145
+ }
1146
+ unconsumedEvents . push ( value ) ;
1147
+ } else unconsumedPromises . shift ( ) . resolve ( createIterResult ( value , false ) ) ;
1107
1148
}
1108
1149
1109
1150
function errorHandler ( err ) {
1110
- finished = true ;
1151
+ if ( unconsumedPromises . isEmpty ( ) ) error = err ;
1152
+ else unconsumedPromises . shift ( ) . reject ( err ) ;
1111
1153
1112
- const toError = ArrayPrototypeShift ( unconsumedPromises ) ;
1154
+ closeHandler ( ) ;
1155
+ }
1113
1156
1114
- if ( toError ) {
1115
- toError . reject ( err ) ;
1116
- } else {
1117
- // The next time we call next()
1118
- error = err ;
1157
+ function closeHandler ( ) {
1158
+ removeAll ( ) ;
1159
+ finished = true ;
1160
+ const doneResult = createIterResult ( undefined , true ) ;
1161
+ while ( ! unconsumedPromises . isEmpty ( ) ) {
1162
+ unconsumedPromises . shift ( ) . resolve ( doneResult ) ;
1119
1163
}
1120
1164
1121
- iterator . return ( ) ;
1165
+ return PromiseResolve ( doneResult ) ;
1122
1166
}
1123
1167
}
1168
+
1169
+ function listenersController ( ) {
1170
+ const listeners = [ ] ;
1171
+
1172
+ return {
1173
+ addEventListener ( emitter , event , handler , flags ) {
1174
+ eventTargetAgnosticAddListener ( emitter , event , handler , flags ) ;
1175
+ ArrayPrototypePush ( listeners , [ emitter , event , handler , flags ] ) ;
1176
+ } ,
1177
+ removeAll ( ) {
1178
+ while ( listeners . length > 0 ) {
1179
+ ReflectApply ( eventTargetAgnosticRemoveListener , undefined , ArrayPrototypePop ( listeners ) ) ;
1180
+ }
1181
+ }
1182
+ } ;
1183
+ }
0 commit comments