@@ -27,6 +27,10 @@ public static IJsCallable CreatePromiseConstructor(JsEngine engine)
2727
2828 promiseConstructor . SetHostedProperty ( "race" , PromiseRace ) ;
2929
30+ promiseConstructor . SetHostedProperty ( "allSettled" , PromiseAllSettled ) ;
31+
32+ promiseConstructor . SetHostedProperty ( "any" , PromiseAny ) ;
33+
3034 if ( promisePrototype is not null )
3135 {
3236 AttachPrototypeMethods ( promisePrototype ) ;
@@ -154,6 +158,14 @@ void AttachPrototypeMethods(JsObject prototype)
154158 object ? PromiseResolve ( IReadOnlyList < object ? > args )
155159 {
156160 var value = args . Count > 0 ? args [ 0 ] : null ;
161+
162+ if ( value is JsObject jsObject && JsPromise . TryGetInternalPromise ( jsObject , out var _ ) &&
163+ jsObject . TryGetProperty ( "constructor" , out var ctor ) &&
164+ ReferenceEquals ( ctor , promiseConstructor ) )
165+ {
166+ return value ;
167+ }
168+
157169 var promise = new JsPromise ( engine ) ;
158170 AssignPromisePrototype ( promise . JsObject ) ;
159171 AddPromiseInstanceMethods ( promise . JsObject , promise , engine ) ;
@@ -329,6 +341,202 @@ HostFunction CreateRaceReject()
329341 return new HostFunction ( Reject ) ;
330342 }
331343 }
344+
345+ object ? PromiseAllSettled ( IReadOnlyList < object ? > args )
346+ {
347+ if ( args . Count == 0 || args [ 0 ] is not JsArray array )
348+ {
349+ return null ;
350+ }
351+
352+ var resultPromise = new JsPromise ( engine ) ;
353+ AssignPromisePrototype ( resultPromise . JsObject ) ;
354+ AddPromiseInstanceMethods ( resultPromise . JsObject , resultPromise , engine ) ;
355+
356+ var remaining = array . Items . Count ;
357+ var results = new object ? [ remaining ] ;
358+
359+ if ( remaining == 0 )
360+ {
361+ resultPromise . Resolve ( new JsArray ( engine . RealmState ) ) ;
362+ return resultPromise . JsObject ;
363+ }
364+
365+ for ( var i = 0 ; i < array . Items . Count ; i ++ )
366+ {
367+ var index = i ;
368+ var item = array . Items [ i ] ;
369+ if ( item is JsObject itemObj && itemObj . TryGetProperty ( "then" , out var thenMethod ) &&
370+ thenMethod is IJsCallable thenCallable )
371+ {
372+ thenCallable . Invoke ( [ CreateResolve ( index ) , CreateReject ( index ) ] , itemObj ) ;
373+ }
374+ else
375+ {
376+ Resolve ( index , item , false ) ;
377+ }
378+ }
379+
380+ return resultPromise . JsObject ;
381+
382+ HostFunction CreateResolve ( int index )
383+ {
384+ object ? ResolveWrapper ( object ? _ , IReadOnlyList < object ? > resolveArgs )
385+ {
386+ Resolve ( index , resolveArgs . Count > 0 ? resolveArgs [ 0 ] : null , false ) ;
387+ return null ;
388+ }
389+
390+ return new HostFunction ( ResolveWrapper ) ;
391+ }
392+
393+ HostFunction CreateReject ( int index )
394+ {
395+ object ? RejectWrapper ( object ? _ , IReadOnlyList < object ? > rejectArgs )
396+ {
397+ Resolve ( index , rejectArgs . Count > 0 ? rejectArgs [ 0 ] : null , true ) ;
398+ return null ;
399+ }
400+
401+ return new HostFunction ( RejectWrapper ) ;
402+ }
403+
404+ void Resolve ( int index , object ? value , bool isRejected )
405+ {
406+ results [ index ] = CreateAllSettledResult ( value , isRejected ) ;
407+ remaining -- ;
408+ if ( remaining != 0 )
409+ {
410+ return ;
411+ }
412+
413+ var resultArray = new JsArray ( engine . RealmState ) ;
414+ foreach ( var result in results )
415+ {
416+ resultArray . Push ( result ) ;
417+ }
418+
419+ resultPromise . Resolve ( resultArray ) ;
420+ }
421+
422+ JsObject CreateAllSettledResult ( object ? value , bool isRejected )
423+ {
424+ var result = new JsObject ( ) ;
425+ if ( engine . RealmState ? . ObjectPrototype is not null )
426+ {
427+ result . SetPrototype ( engine . RealmState . ObjectPrototype ) ;
428+ }
429+
430+ result . SetProperty ( "status" , isRejected ? "rejected" : "fulfilled" ) ;
431+ result . SetProperty ( isRejected ? "reason" : "value" , value ) ;
432+ return result ;
433+ }
434+ }
435+
436+ object ? PromiseAny ( IReadOnlyList < object ? > args )
437+ {
438+ if ( args . Count == 0 || args [ 0 ] is not JsArray array )
439+ {
440+ return null ;
441+ }
442+
443+ var resultPromise = new JsPromise ( engine ) ;
444+ AssignPromisePrototype ( resultPromise . JsObject ) ;
445+ AddPromiseInstanceMethods ( resultPromise . JsObject , resultPromise , engine ) ;
446+
447+ var errors = new JsArray ( engine . RealmState ) ;
448+ var remaining = array . Items . Count ;
449+ var resolved = false ;
450+
451+ if ( remaining == 0 )
452+ {
453+ resultPromise . Reject ( CreateAggregateError ( errors ) ) ;
454+ return resultPromise . JsObject ;
455+ }
456+
457+ for ( var i = 0 ; i < array . Items . Count ; i ++ )
458+ {
459+ var item = array . Items [ i ] ;
460+ if ( item is JsObject itemObj && itemObj . TryGetProperty ( "then" , out var thenMethod ) &&
461+ thenMethod is IJsCallable thenCallable )
462+ {
463+ thenCallable . Invoke ( [ CreateResolve ( ) , CreateReject ( ) ] , itemObj ) ;
464+ }
465+ else
466+ {
467+ Resolve ( item ) ;
468+ }
469+ }
470+
471+ return resultPromise . JsObject ;
472+
473+ HostFunction CreateResolve ( )
474+ {
475+ object ? ResolveWrapper ( object ? _ , IReadOnlyList < object ? > resolveArgs )
476+ {
477+ Resolve ( resolveArgs . Count > 0 ? resolveArgs [ 0 ] : null ) ;
478+ return null ;
479+ }
480+
481+ return new HostFunction ( ResolveWrapper ) ;
482+ }
483+
484+ HostFunction CreateReject ( )
485+ {
486+ object ? RejectWrapper ( object ? _ , IReadOnlyList < object ? > rejectArgs )
487+ {
488+ Reject ( rejectArgs . Count > 0 ? rejectArgs [ 0 ] : null ) ;
489+ return null ;
490+ }
491+
492+ return new HostFunction ( RejectWrapper ) ;
493+ }
494+
495+ void Resolve ( object ? value )
496+ {
497+ if ( resolved )
498+ {
499+ return ;
500+ }
501+
502+ resolved = true ;
503+ resultPromise . Resolve ( value ) ;
504+ }
505+
506+ void Reject ( object ? reason )
507+ {
508+ if ( resolved )
509+ {
510+ return ;
511+ }
512+
513+ errors . Push ( reason ) ;
514+ remaining -- ;
515+ if ( remaining == 0 )
516+ {
517+ resultPromise . Reject ( CreateAggregateError ( errors ) ) ;
518+ }
519+ }
520+
521+ object CreateAggregateError ( JsArray rejectionErrors )
522+ {
523+ if ( engine . GlobalObject . TryGetProperty ( "AggregateError" , out var aggregateCtor ) &&
524+ aggregateCtor is IJsCallable callable )
525+ {
526+ try
527+ {
528+ return callable . Invoke ( [ rejectionErrors , "All promises were rejected" ] , null ) ??
529+ rejectionErrors ;
530+ }
531+ catch
532+ {
533+ // Fall through to returning the errors array
534+ }
535+ }
536+
537+ return rejectionErrors ;
538+ }
539+ }
332540 }
333541
334542 /// <summary>
0 commit comments