@@ -52,6 +52,9 @@ export type EventHandlerMap<Events extends EventMap> = Map<
5252 EventHandlerList < Events [ keyof Events ] > | WildCardEventHandlerList < Events >
5353>
5454
55+ /** What one handler made of an event: what it returned, or what it threw. */
56+ export type HandlerOutcome = { ok : true , value : unknown } | { ok : false , error : Error }
57+
5558export interface Emitter < Events extends EventMap > {
5659 /** Underlying handler map. Mutating it directly is supported but rarely needed. */
5760 all : EventHandlerMap < Events >
@@ -98,6 +101,16 @@ export interface Emitter<Events extends EventMap> {
98101 */
99102 emitAndCollect : < Key extends keyof Events > ( _type : Key , _event : Events [ Key ] ) => Promise < Array < { ok : true , value : unknown } | { ok : false , error : Error } > >
100103
104+ /**
105+ * `emitAndCollect`, answering in place when it can: the outcomes themselves
106+ * when every handler returned a value, and a promise of them from the first
107+ * handler that returned a promise. Handlers run in the same order either
108+ * way, one after the other. For a caller that is itself synchronous unless
109+ * something makes it wait, such as a model write asking its `creating`
110+ * listeners whether to go ahead.
111+ */
112+ emitAndCollectInPlace : < Key extends keyof Events > ( _type : Key , _event : Events [ Key ] ) => HandlerOutcome [ ] | Promise < HandlerOutcome [ ] >
113+
101114 /** Drop every handler for a type (or every handler everywhere when omitted). */
102115 removeAllListeners : ( _type ?: keyof Events | '*' ) => void
103116
@@ -383,7 +396,62 @@ export function createEmitter<Events extends EventMap>(
383396 return results
384397 }
385398
386- return { all : map , on, once, off, emit, emitAsync, emitAndCollect, removeAllListeners, listenerCount } as Emitter < Events >
399+ function emitAndCollectInPlace ( type : any , evt ?: any ) : HandlerOutcome [ ] | Promise < HandlerOutcome [ ] > {
400+ // Every matching handler, in dispatch order: exact, then patterns, then '*'.
401+ const calls : Array < ( ) => unknown > = [ ]
402+ const queue = ( handlers : Handler < any > [ ] | WildcardHandler < any > [ ] | undefined , isWildcard : boolean ) : void => {
403+ if ( ! handlers ) return
404+ for ( const handler of sortByPriority ( priorities , handlers ) )
405+ calls . push ( isWildcard ? ( ) => ( handler as WildcardHandler < any > ) ( type , evt ) : ( ) => ( handler as Handler < any > ) ( evt ) )
406+ }
407+ queue ( map . get ( type ) as Handler < any > [ ] | undefined , false )
408+ const typeStr = String ( type )
409+ map . forEach ( ( patternHandlers , key ) => {
410+ const keyStr = String ( key )
411+ if ( keyStr === typeStr || keyStr === '*' || ! keyStr . includes ( '*' ) ) return
412+ if ( matchPattern ( keyStr , typeStr , patternHandlers ) ) queue ( patternHandlers as WildcardHandler < any > [ ] , true )
413+ } )
414+ queue ( map . get ( '*' ) as WildcardHandler < any > [ ] | undefined , true )
415+
416+ const outcomes : HandlerOutcome [ ] = [ ]
417+ const failed = ( err : unknown ) : HandlerOutcome => ( { ok : false , error : err instanceof Error ? err : new Error ( String ( err ) ) } )
418+
419+ // From the first handler that has to be waited for, the rest are waited
420+ // for too, each after the one before it, as `emitAndCollect` does.
421+ const later = async ( from : number , pending : Promise < unknown > ) : Promise < HandlerOutcome [ ] > => {
422+ try {
423+ outcomes . push ( { ok : true , value : await pending } )
424+ }
425+ catch ( err ) {
426+ outcomes . push ( failed ( err ) )
427+ }
428+ for ( let index = from + 1 ; index < calls . length ; index ++ ) {
429+ try {
430+ const result = calls [ index ] ! ( )
431+ outcomes . push ( { ok : true , value : isPromiseLike ( result ) ? await result : result } )
432+ }
433+ catch ( err ) {
434+ outcomes . push ( failed ( err ) )
435+ }
436+ }
437+ return outcomes
438+ }
439+
440+ for ( let index = 0 ; index < calls . length ; index ++ ) {
441+ try {
442+ const result = calls [ index ] ! ( )
443+ if ( isPromiseLike ( result ) )
444+ return later ( index , result )
445+ outcomes . push ( { ok : true , value : result } )
446+ }
447+ catch ( err ) {
448+ outcomes . push ( failed ( err ) )
449+ }
450+ }
451+ return outcomes
452+ }
453+
454+ return { all : map , on, once, off, emit, emitAsync, emitAndCollect, emitAndCollectInPlace, removeAllListeners, listenerCount } as Emitter < Events >
387455}
388456
389457/**
@@ -696,6 +764,7 @@ type Listen = <Key extends keyof StacksEvents>(_type: Key, _handler: Handler<Sta
696764type Off = < Key extends keyof StacksEvents > ( _type : Key , handler ?: Handler < StacksEvents [ Key ] > ) => void
697765type DispatchAsync = < Key extends keyof StacksEvents > ( _type : Key , _event : StacksEvents [ Key ] ) => Promise < unknown [ ] >
698766type DispatchAndCollect = < Key extends keyof StacksEvents > ( _type : Key , _event : StacksEvents [ Key ] ) => Promise < Array < { ok : true , value : unknown } | { ok : false , error : Error } > >
767+ type DispatchAndCollectInPlace = < Key extends keyof StacksEvents > ( _type : Key , _event : StacksEvents [ Key ] ) => HandlerOutcome [ ] | Promise < HandlerOutcome [ ] >
699768
700769const emitter : Emitter < StacksEvents > = events
701770const useEvents : Emitter < StacksEvents > = events
@@ -737,6 +806,11 @@ const dispatchAndCollect: DispatchAndCollect = (type, event) => {
737806 observe ( type )
738807 return emitter . emitAndCollect ( type , event )
739808}
809+ /** `dispatchAndCollect`, answering in place when every listener did (see `emitAndCollectInPlace`). */
810+ const dispatchAndCollectInPlace : DispatchAndCollectInPlace = ( type , event ) => {
811+ observe ( type )
812+ return emitter . emitAndCollectInPlace ( type , event )
813+ }
740814const useEvent : Dispatch = dispatch
741815const all : EventHandlerMap < StacksEvents > = emitter . all
742816const listen : Listen = emitter . on
@@ -748,6 +822,7 @@ export {
748822 all ,
749823 dispatch ,
750824 dispatchAndCollect ,
825+ dispatchAndCollectInPlace ,
751826 dispatchAsync ,
752827 emitter ,
753828 events ,
0 commit comments