33const {
44 ArrayPrototypePush,
55 Boolean,
6+ FunctionPrototypeCall,
67 MathFloor,
78 Number,
89 NumberIsNaN,
@@ -12,6 +13,7 @@ const {
1213 PromisePrototypeThen,
1314 PromiseReject,
1415 PromiseResolve,
16+ PromiseWithResolvers,
1517 ReflectApply,
1618 Symbol,
1719} = primordials ;
@@ -20,6 +22,7 @@ const { AbortController, AbortSignal } = require('internal/abort_controller');
2022
2123const {
2224 AbortError,
25+ aggregateTwoErrors,
2326 codes : {
2427 ERR_ILLEGAL_CONSTRUCTOR ,
2528 ERR_MISSING_ARGS ,
@@ -31,15 +34,45 @@ const {
3134 validateInteger,
3235 validateObject,
3336 validateFunction,
37+ validateBoolean,
3438} = require ( 'internal/validators' ) ;
3539const { kWeakHandler, kResistStopPropagation } = require ( 'internal/event_target' ) ;
3640const destroyImpl = require ( 'internal/streams/destroy' ) ;
37- const { finished } = require ( 'internal/streams/end-of-stream' ) ;
41+ const { eos , finished } = require ( 'internal/streams/end-of-stream' ) ;
3842const Stream = require ( 'stream' ) ;
3943
4044const kEmpty = Symbol ( 'kEmpty' ) ;
4145const kEof = Symbol ( 'kEof' ) ;
4246
47+ const {
48+ isPromise,
49+ } = require ( 'internal/util/types' ) ;
50+
51+
52+ // Return native promises unchanged and normalize custom thenables exactly once.
53+ // Capturing `then` avoids repeated getter access, while calling it with `value`
54+ // as the receiver preserves thenables that depend on their `this` value.
55+ function getThenablePromise ( value ) {
56+ if ( isPromise ( value ) ) {
57+ return value ;
58+ }
59+
60+ const valueType = typeof value ;
61+ if ( ( valueType === 'object' && value !== null ) || valueType === 'function' ) {
62+ const then = value . then ;
63+ if ( typeof then === 'function' ) {
64+ return PromiseResolve ( {
65+ __proto__ : null ,
66+ then ( resolve , reject ) {
67+ FunctionPrototypeCall ( then , value , resolve , reject ) ;
68+ } ,
69+ } ) ;
70+ }
71+ }
72+
73+ return undefined ;
74+ }
75+
4376function map ( fn , options ) {
4477 validateFunction ( fn , 'fn' ) ;
4578 if ( options != null ) {
@@ -188,36 +221,208 @@ function map(fn, options) {
188221 } . call ( this ) ;
189222}
190223
191- async function some ( fn , options = undefined ) {
192- for await ( const unused of filter . call ( this , fn , options ) ) {
193- return true ;
224+ function nowOrLater ( fn , fn2 , args ) {
225+ const value = fn ( ...args ) ;
226+ const promise = getThenablePromise ( value ) ;
227+ if ( promise !== undefined ) {
228+ return PromisePrototypeThen ( promise , fn2 ) ;
194229 }
195- return false ;
230+ return fn2 ( value ) ;
231+ }
232+
233+ async function some ( fn , options = undefined ) {
234+ validateFunction ( fn , 'fn' ) ;
235+ const someFn = ( ...args ) => {
236+ return nowOrLater ( fn , Boolean , args ) ;
237+ } ;
238+ return ( await find . call ( this , someFn , options ) ) !== undefined ;
196239}
197240
198241async function every ( fn , options = undefined ) {
199242 validateFunction ( fn , 'fn' ) ;
243+ const everyFn = ( ...args ) => {
244+ return nowOrLater ( fn , ( value ) => ! value , args ) ;
245+ } ;
200246 // https://en.wikipedia.org/wiki/De_Morgan%27s_laws
201- return ! ( await some . call ( this , async ( ...args ) => {
202- return ! ( await fn ( ...args ) ) ;
203- } , options ) ) ;
247+ return ! ( await find . call ( this , everyFn , options ) ) ;
204248}
205249
206- async function find ( fn , options ) {
207- for await ( const result of filter . call ( this , fn , options ) ) {
208- return result ;
250+ function find ( fn , options ) {
251+ validateFunction ( fn , 'fn' ) ;
252+
253+ if ( options != null ) {
254+ validateObject ( options , 'options' ) ;
209255 }
210- return undefined ;
256+ const signal = options ?. signal ;
257+ if ( signal != null ) {
258+ validateAbortSignal ( signal , 'options.signal' ) ;
259+ }
260+
261+ const concurrency = MathFloor ( options ?. concurrency ?? 1 ) ;
262+ validateInteger ( concurrency , 'options.concurrency' , 1 ) ;
263+
264+ const destroyOnReturn = options ?. destroyOnReturn ?? true ;
265+ validateBoolean ( destroyOnReturn , 'options.destroyOnReturn' ) ;
266+
267+ const ac = new AbortController ( ) ;
268+ const predicateSignal = AbortSignal . any ( [ ac . signal , signal ] . filter ( Boolean ) ) ;
269+ const predicateOptions = { signal : predicateSignal } ;
270+
271+ // Concurrent predicates can settle out of order. Stop reading after any
272+ // match, but keep the lowest index after all active predicates settle.
273+ const stream = this ;
274+ const { promise, resolve } = PromiseWithResolvers ( ) ;
275+ let match ;
276+ let error ;
277+ let activeEvaluations = 0 ;
278+ let nextIndex = 0 ;
279+ let ended = false ;
280+ let settled = false ;
281+ let draining = false ;
282+
283+ function settle ( ) {
284+ if ( ! settled ) {
285+ settled = true ;
286+ resolve ( ) ;
287+ }
288+ }
289+
290+ function fail ( err ) {
291+ if ( settled ) {
292+ return ;
293+ }
294+ error = aggregateTwoErrors ( error , err ) ;
295+ destroyImpl . destroyer ( stream , error ) ;
296+ settle ( ) ;
297+ }
298+
299+ function maybeSettle ( ) {
300+ if ( activeEvaluations === 0 && ( match !== undefined || ended ) ) {
301+ settle ( ) ;
302+ }
303+ }
304+
305+ function evaluationFinished ( matches , chunk , index ) {
306+ if ( matches && ( match === undefined || index < match . index ) ) {
307+ match = { index, value : chunk } ;
308+ }
309+ activeEvaluations -- ;
310+ maybeSettle ( ) ;
311+
312+ if ( ! settled && match === undefined && ! draining ) {
313+ onReadable ( ) ;
314+ }
315+ }
316+
317+ function evaluationRejected ( err ) {
318+ activeEvaluations -- ;
319+ fail ( err ) ;
320+ }
321+
322+ function evaluate ( chunk , index ) {
323+ activeEvaluations ++ ;
324+
325+ let matches ;
326+ try {
327+ matches = fn ( chunk , predicateOptions ) ;
328+ const matchesPromise = getThenablePromise ( matches ) ;
329+ if ( matchesPromise !== undefined ) {
330+ PromisePrototypeThen (
331+ matchesPromise ,
332+ ( result ) => evaluationFinished ( result , chunk , index ) ,
333+ evaluationRejected ,
334+ ) ;
335+ return ;
336+ }
337+ } catch ( err ) {
338+ evaluationRejected ( err ) ;
339+ return ;
340+ }
341+ evaluationFinished ( matches , chunk , index ) ;
342+ }
343+
344+ function onReadable ( ) {
345+ if ( draining || settled || match !== undefined ) {
346+ return ;
347+ }
348+
349+ draining = true ;
350+ try {
351+ while (
352+ ! settled &&
353+ match === undefined &&
354+ activeEvaluations < concurrency
355+ ) {
356+ if ( signal ?. aborted ) {
357+ fail ( new AbortError ( undefined , { cause : signal . reason } ) ) ;
358+ return ;
359+ }
360+
361+ const chunk = stream . destroyed ? null : stream . read ( ) ;
362+ if ( chunk === null ) {
363+ return ;
364+ }
365+ evaluate ( chunk , nextIndex ++ ) ;
366+ }
367+ } catch ( err ) {
368+ fail ( err ) ;
369+ } finally {
370+ draining = false ;
371+ }
372+ }
373+
374+ function onAbort ( ) {
375+ fail ( new AbortError ( undefined , { cause : signal . reason } ) ) ;
376+ }
377+
378+ stream . on ( 'readable' , onReadable ) ;
379+
380+ const cleanup = eos ( stream , { writable : false } , ( err ) => {
381+ if ( settled ) {
382+ return ;
383+ }
384+ if ( err ) {
385+ fail ( err ) ;
386+ return ;
387+ }
388+ ended = true ;
389+ maybeSettle ( ) ;
390+ } ) ;
391+
392+ if ( signal != null ) {
393+ signal . addEventListener ( 'abort' , onAbort , { once : true } ) ;
394+ if ( signal . aborted ) {
395+ onAbort ( ) ;
396+ }
397+ }
398+
399+ return PromisePrototypeThen ( promise , ( ) => {
400+ stream . off ( 'readable' , onReadable ) ;
401+ signal ?. removeEventListener ( 'abort' , onAbort ) ;
402+ ac . abort ( ) ;
403+
404+ if (
405+ ( error || destroyOnReturn !== false ) &&
406+ ( error === undefined || stream . _readableState . autoDestroy )
407+ ) {
408+ destroyImpl . destroyer ( stream , error ) ;
409+ } else {
410+ cleanup ( ) ;
411+ }
412+
413+ if ( error ) {
414+ return PromiseReject ( error ) ;
415+ }
416+ return match ?. value ;
417+ } ) ;
211418}
212419
213420async function forEach ( fn , options ) {
214421 validateFunction ( fn , 'fn' ) ;
215- async function forEachFn ( value , options ) {
216- await fn ( value , options ) ;
217- return kEmpty ;
218- }
219- // eslint-disable-next-line no-unused-vars
220- for await ( const unused of map . call ( this , forEachFn , options ) ) ;
422+ const forEachFn = ( ...args ) => {
423+ return nowOrLater ( fn , ( ) => false , args ) ;
424+ } ;
425+ await find . call ( this , forEachFn , options ) ;
221426}
222427
223428function filter ( fn , options ) {
0 commit comments