0
# Events and Messaging
1
2
Theia's event system provides type-safe event handling with emitters, async event support, cancellation tokens, and disposable subscriptions for building reactive applications.
3
4
## Capabilities
5
6
### Event Interface
7
8
Core event subscription interface for type-safe event handling.
9
10
```typescript { .api }
11
/**
12
* Represents an event that can be subscribed to
13
*/
14
interface Event<T> {
15
/**
16
* Subscribe to the event
17
* @param listener - Function to call when event fires
18
* @param thisArgs - Optional 'this' context for listener
19
* @param disposables - Optional array to add subscription disposable to
20
* @returns Disposable to unsubscribe
21
*/
22
(listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable;
23
}
24
```
25
26
### Event Emitter
27
28
Creates and manages events with type safety and subscription management.
29
30
```typescript { .api }
31
/**
32
* Event emitter for firing events to subscribers
33
*/
34
class Emitter<T> {
35
/**
36
* The event that subscribers can listen to
37
*/
38
readonly event: Event<T>;
39
40
/**
41
* Create emitter with optional configuration
42
* @param options - Emitter configuration options
43
*/
44
constructor(options?: EmitterOptions);
45
46
/**
47
* Fire the event to all subscribers
48
* @param event - Event data to send
49
*/
50
fire(event: T): void;
51
52
/**
53
* Process each listener one by one
54
* @param processor - Function that processes each listener, returns false to stop
55
* @returns Promise that resolves when processing is complete
56
*/
57
sequence(processor: (listener: (e: T) => any) => MaybePromise<boolean>): Promise<void>;
58
59
/**
60
* Dispose the emitter and all subscriptions
61
*/
62
dispose(): void;
63
}
64
65
/**
66
* Configuration options for event emitters
67
*/
68
interface EmitterOptions {
69
/** Called when first listener is added */
70
onFirstListenerAdd?: Function;
71
/** Called when last listener is removed */
72
onLastListenerRemove?: Function;
73
/** Threshold for memory leak warnings */
74
leakWarningThreshold?: number;
75
}
76
```
77
78
**Usage Example:**
79
80
```typescript
81
import { Emitter, Event } from "@theia/core";
82
83
class FileWatcher {
84
private readonly onDidChangeEmitter = new Emitter<string>();
85
86
/** Event fired when file changes */
87
readonly onDidChange: Event<string> = this.onDidChangeEmitter.event;
88
89
private watchFile(path: string): void {
90
// File watching logic
91
// When file changes:
92
this.onDidChangeEmitter.fire(path);
93
}
94
95
dispose(): void {
96
this.onDidChangeEmitter.dispose();
97
}
98
}
99
100
// Usage
101
const watcher = new FileWatcher();
102
const subscription = watcher.onDidChange(path => {
103
console.log(`File changed: ${path}`);
104
});
105
106
// Later: subscription.dispose();
107
```
108
109
### Async Event Emitter
110
111
Handles asynchronous event processing with Promise support.
112
113
```typescript { .api }
114
/**
115
* Emitter for async events that return promises
116
*/
117
class AsyncEmitter<T> {
118
readonly event: Event<T>;
119
120
constructor(options?: EmitterOptions);
121
122
/**
123
* Fire async event and wait for all handlers
124
* @param event - Event data
125
* @returns Promise that resolves when all handlers complete
126
*/
127
fireAndAwait(event: T): Promise<void>;
128
129
dispose(): void;
130
}
131
```
132
133
### Queueable Emitter
134
135
Emitter that queues events and fires them in batches.
136
137
```typescript { .api }
138
/**
139
* Emitter that can queue events and fire them as arrays
140
*/
141
class QueueableEmitter<T> extends Emitter<T[]> {
142
/** Current queue of events to fire */
143
currentQueue?: T[];
144
145
constructor(options?: EmitterOptions);
146
147
/**
148
* Queue events to be fired together
149
* @param arg - Events to queue
150
*/
151
queue(...arg: T[]): void;
152
153
/**
154
* Fire all queued events as an array
155
*/
156
fire(): void;
157
158
dispose(): void;
159
}
160
```
161
162
### Wait Until Event
163
164
Special event type for handling async operations with wait semantics.
165
166
```typescript { .api }
167
/**
168
* Event that allows listeners to delay completion
169
*/
170
interface WaitUntilEvent {
171
/**
172
* Register a promise that must complete before event finishes
173
* @param thenable - Promise to wait for
174
*/
175
waitUntil(thenable: Promise<any>): void;
176
}
177
178
namespace WaitUntilEvent {
179
/**
180
* Fire a wait-until event
181
* @param emitter - Event emitter
182
* @param event - Event data
183
* @returns Promise that waits for all waitUntil promises
184
*/
185
function fire<T>(emitter: Emitter<T & WaitUntilEvent>, event: T): Promise<void>;
186
}
187
```
188
189
**Usage Example:**
190
191
```typescript
192
import { Emitter, WaitUntilEvent } from "@theia/core";
193
194
interface BeforeSaveEvent extends WaitUntilEvent {
195
readonly uri: string;
196
}
197
198
class DocumentManager {
199
private readonly onWillSaveEmitter = new Emitter<BeforeSaveEvent>();
200
201
async save(uri: string): Promise<void> {
202
// Fire event and wait for all handlers
203
await WaitUntilEvent.fire(this.onWillSaveEmitter, { uri });
204
205
// Now perform actual save
206
await this.doSave(uri);
207
}
208
209
private async doSave(uri: string): Promise<void> {
210
// Actual save implementation
211
}
212
}
213
214
// Extensions can delay save
215
documentManager.onWillSave(event => {
216
if (event.uri.endsWith('.ts')) {
217
// Format before save
218
event.waitUntil(this.formatTypeScript(event.uri));
219
}
220
});
221
```
222
223
### Cancellation System
224
225
Provides cancellation tokens for long-running operations.
226
227
```typescript { .api }
228
/**
229
* Token that signals when operation should be cancelled
230
*/
231
interface CancellationToken {
232
/** True if cancellation has been requested */
233
readonly isCancellationRequested: boolean;
234
235
/** Event fired when cancellation is requested */
236
readonly onCancellationRequested: Event<any>;
237
}
238
239
/**
240
* Creates cancellation tokens
241
*/
242
class CancellationTokenSource {
243
/** The cancellation token */
244
readonly token: CancellationToken;
245
246
/** Request cancellation */
247
cancel(): void;
248
249
/** Dispose the token source */
250
dispose(): void;
251
}
252
253
/**
254
* Error thrown when operation is cancelled
255
*/
256
class CancellationError extends Error {
257
constructor();
258
}
259
260
/**
261
* Pre-defined tokens
262
*/
263
namespace CancellationToken {
264
/** Token that is never cancelled */
265
const None: CancellationToken;
266
267
/** Token that is already cancelled */
268
const Cancelled: CancellationToken;
269
}
270
```
271
272
**Usage Example:**
273
274
```typescript
275
import { CancellationToken, CancellationTokenSource, CancellationError } from "@theia/core";
276
277
class SearchService {
278
async search(query: string, token: CancellationToken): Promise<string[]> {
279
const results: string[] = [];
280
281
for (let i = 0; i < 1000; i++) {
282
// Check for cancellation
283
if (token.isCancellationRequested) {
284
throw new CancellationError();
285
}
286
287
// Simulate work
288
await this.searchChunk(query, i);
289
results.push(`result-${i}`);
290
}
291
292
return results;
293
}
294
295
private async searchChunk(query: string, chunk: number): Promise<void> {
296
// Simulate async work
297
await new Promise(resolve => setTimeout(resolve, 10));
298
}
299
}
300
301
// Usage with timeout
302
async function searchWithTimeout(service: SearchService, query: string): Promise<string[]> {
303
const source = new CancellationTokenSource();
304
305
// Cancel after 5 seconds
306
setTimeout(() => source.cancel(), 5000);
307
308
try {
309
return await service.search(query, source.token);
310
} finally {
311
source.dispose();
312
}
313
}
314
```
315
316
### Event Utilities
317
318
Utility functions for working with events.
319
320
```typescript { .api }
321
namespace Event {
322
/** Event that never fires */
323
const None: Event<any>;
324
325
/**
326
* Get the maximum number of listeners for an event
327
* @param event - Event to check
328
* @returns Maximum listeners count
329
*/
330
function getMaxListeners(event: Event<unknown>): number;
331
332
/**
333
* Set the maximum number of listeners for an event
334
* @param event - Event to configure
335
* @param maxListeners - Maximum listeners count
336
* @returns The set maximum listeners count
337
*/
338
function setMaxListeners<N extends number>(event: Event<unknown>, maxListeners: N): N;
339
340
/**
341
* Add to the maximum number of listeners for an event
342
* @param event - Event to configure
343
* @param add - Number to add to current max
344
* @returns New maximum listeners count
345
*/
346
function addMaxListeners(event: Event<unknown>, add: number): number;
347
348
/**
349
* Create event that fires only once
350
* @param event - Source event
351
* @returns Event that fires once then disposes
352
*/
353
function once<T>(event: Event<T>): Event<T>;
354
355
/**
356
* Convert event to promise that resolves on first emission
357
* @param event - Source event
358
* @returns Promise that resolves with event data
359
*/
360
function toPromise<T>(event: Event<T>): Promise<T>;
361
362
/**
363
* Transform event data
364
* @param event - Source event
365
* @param map - Transformation function
366
* @returns Event with transformed data
367
*/
368
function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O>;
369
370
/**
371
* Filter event data
372
* @param event - Source event
373
* @param filter - Filter predicate
374
* @returns Event that only fires when filter returns true
375
*/
376
function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T>;
377
378
/**
379
* Combine multiple events into single event
380
* @param events - Array of events to combine
381
* @returns Event that fires when any source event fires
382
*/
383
function any<T>(...events: Event<T>[]): Event<T>;
384
function any(...events: Event<any>[]): Event<void>;
385
386
/**
387
* Debounce event firing
388
* @param event - Source event
389
* @param delay - Debounce delay in milliseconds
390
* @returns Debounced event
391
*/
392
function debounce<T>(event: Event<T>, delay: number): Event<T>;
393
}
394
```
395
396
**Usage Example:**
397
398
```typescript
399
import { Event } from "@theia/core";
400
401
class ConfigurationWatcher {
402
private readonly onDidChangeEmitter = new Emitter<string>();
403
404
// Transform and filter events
405
readonly onImportantConfigChange = Event.filter(
406
Event.map(this.onDidChangeEmitter.event, path => path.toLowerCase()),
407
path => path.includes('important')
408
);
409
410
// Debounced events for high-frequency changes
411
readonly onConfigChangeDebounced = Event.debounce(
412
this.onDidChangeEmitter.event,
413
500 // 500ms debounce
414
);
415
}
416
```
417
418
### Message Service
419
420
High-level service for displaying user messages and dialogs.
421
422
```typescript { .api }
423
/**
424
* Service for showing messages to users
425
*/
426
interface MessageService {
427
/**
428
* Show info message
429
* @param message - Message text
430
* @param actions - Optional actions
431
* @returns Promise resolving to selected action
432
*/
433
info(message: string, ...actions: string[]): Promise<string | undefined>;
434
435
/**
436
* Show warning message
437
* @param message - Message text
438
* @param actions - Optional actions
439
* @returns Promise resolving to selected action
440
*/
441
warn(message: string, ...actions: string[]): Promise<string | undefined>;
442
443
/**
444
* Show error message
445
* @param message - Message text
446
* @param actions - Optional actions
447
* @returns Promise resolving to selected action
448
*/
449
error(message: string, ...actions: string[]): Promise<string | undefined>;
450
}
451
452
/**
453
* Service token for MessageService
454
*/
455
const MessageService: symbol;
456
```
457
458
## Types
459
460
```typescript { .api }
461
type EventListener<T> = (e: T) => any;
462
463
type MaybePromise<T> = T | Promise<T>;
464
465
interface Disposable {
466
dispose(): void;
467
}
468
469
interface DisposableCollection extends Disposable {
470
push(disposable: Disposable): Disposable;
471
}
472
```