0
# Promise Delegation
1
2
Promise wrapper that separates promise creation from resolution logic, enabling flexible asynchronous workflows where the resolution logic cannot be defined at the point of promise creation.
3
4
## Capabilities
5
6
### PromiseDelegate Class
7
8
A class which wraps a promise into a delegate object, allowing external resolution or rejection of the promise.
9
10
```typescript { .api }
11
/**
12
* A class which wraps a promise into a delegate object.
13
*
14
* Notes:
15
* This class is useful when the logic to resolve or reject a promise
16
* cannot be defined at the point where the promise is created.
17
*/
18
class PromiseDelegate<T> {
19
/**
20
* Construct a new promise delegate
21
*/
22
constructor();
23
24
/**
25
* The promise wrapped by the delegate
26
*/
27
readonly promise: Promise<T>;
28
29
/**
30
* Resolve the wrapped promise with the given value
31
* @param value - The value to use for resolving the promise
32
*/
33
resolve(value: T | PromiseLike<T>): void;
34
35
/**
36
* Reject the wrapped promise with the given value
37
* @param reason - The reason for rejecting the promise
38
*/
39
reject(reason: any): void;
40
}
41
```
42
43
**Usage Examples:**
44
45
```typescript
46
import { PromiseDelegate } from "@lumino/coreutils";
47
48
// Basic usage - create a delegate and resolve it later
49
const delegate = new PromiseDelegate<string>();
50
51
// The promise can be awaited or chained
52
delegate.promise.then(value => {
53
console.log("Resolved with:", value);
54
}).catch(error => {
55
console.log("Rejected with:", error);
56
});
57
58
// Later, resolve the promise from elsewhere
59
setTimeout(() => {
60
delegate.resolve("Hello, World!");
61
}, 1000);
62
63
// Or reject it
64
// delegate.reject(new Error("Something went wrong"));
65
```
66
67
### Common Use Cases
68
69
**Async Event Handling:**
70
71
```typescript
72
import { PromiseDelegate } from "@lumino/coreutils";
73
74
class EventEmitter {
75
private listeners = new Map<string, PromiseDelegate<any>[]>();
76
77
// Wait for a specific event to occur
78
waitForEvent<T>(eventName: string): Promise<T> {
79
const delegate = new PromiseDelegate<T>();
80
81
if (!this.listeners.has(eventName)) {
82
this.listeners.set(eventName, []);
83
}
84
this.listeners.get(eventName)!.push(delegate);
85
86
return delegate.promise;
87
}
88
89
// Emit an event, resolving all waiting promises
90
emit<T>(eventName: string, data: T): void {
91
const delegates = this.listeners.get(eventName) || [];
92
delegates.forEach(delegate => delegate.resolve(data));
93
this.listeners.set(eventName, []);
94
}
95
}
96
97
// Usage
98
const emitter = new EventEmitter();
99
const dataPromise = emitter.waitForEvent<{id: number, name: string}>("user-loaded");
100
101
dataPromise.then(userData => {
102
console.log("User loaded:", userData.name);
103
});
104
105
// Later, in another part of the application
106
emitter.emit("user-loaded", { id: 123, name: "Alice" });
107
```
108
109
**Resource Loading:**
110
111
```typescript
112
import { PromiseDelegate } from "@lumino/coreutils";
113
114
class ResourceLoader {
115
private loadingPromises = new Map<string, PromiseDelegate<any>>();
116
117
loadResource<T>(url: string): Promise<T> {
118
// Return existing promise if already loading
119
if (this.loadingPromises.has(url)) {
120
return this.loadingPromises.get(url)!.promise;
121
}
122
123
// Create new delegate for this resource
124
const delegate = new PromiseDelegate<T>();
125
this.loadingPromises.set(url, delegate);
126
127
// Start loading (could be HTTP request, file read, etc.)
128
this.startLoading(url, delegate);
129
130
return delegate.promise;
131
}
132
133
private startLoading<T>(url: string, delegate: PromiseDelegate<T>): void {
134
// Simulate async loading
135
fetch(url)
136
.then(response => response.json())
137
.then(data => {
138
delegate.resolve(data);
139
this.loadingPromises.delete(url);
140
})
141
.catch(error => {
142
delegate.reject(error);
143
this.loadingPromises.delete(url);
144
});
145
}
146
}
147
148
// Usage
149
const loader = new ResourceLoader();
150
const userPromise = loader.loadResource<{name: string, email: string}>("/api/user/123");
151
const configPromise = loader.loadResource<{theme: string, locale: string}>("/api/config");
152
153
Promise.all([userPromise, configPromise]).then(([user, config]) => {
154
console.log(`Hello ${user.name}, your theme is ${config.theme}`);
155
});
156
```
157
158
**Conditional Resolution:**
159
160
```typescript
161
import { PromiseDelegate } from "@lumino/coreutils";
162
163
class ConditionalTask<T> {
164
private delegate = new PromiseDelegate<T>();
165
private conditions: (() => boolean)[] = [];
166
167
constructor() {}
168
169
get promise(): Promise<T> {
170
return this.delegate.promise;
171
}
172
173
addCondition(condition: () => boolean): this {
174
this.conditions.push(condition);
175
this.checkConditions();
176
return this;
177
}
178
179
setResult(result: T): void {
180
this.result = result;
181
this.checkConditions();
182
}
183
184
setError(error: any): void {
185
this.delegate.reject(error);
186
}
187
188
private result?: T;
189
190
private checkConditions(): void {
191
if (this.result !== undefined && this.conditions.every(cond => cond())) {
192
this.delegate.resolve(this.result);
193
}
194
}
195
}
196
197
// Usage
198
const task = new ConditionalTask<string>();
199
200
let userLoggedIn = false;
201
let dataLoaded = false;
202
203
task
204
.addCondition(() => userLoggedIn)
205
.addCondition(() => dataLoaded);
206
207
task.promise.then(result => {
208
console.log("All conditions met:", result);
209
});
210
211
// Simulate conditions being met
212
setTimeout(() => {
213
userLoggedIn = true;
214
task.setResult("Task completed!");
215
}, 1000);
216
217
setTimeout(() => {
218
dataLoaded = true;
219
}, 1500);
220
```
221
222
**Timeout with Cleanup:**
223
224
```typescript
225
import { PromiseDelegate } from "@lumino/coreutils";
226
227
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
228
const delegate = new PromiseDelegate<T>();
229
let timeoutId: number;
230
231
// Race between the original promise and timeout
232
promise.then(
233
value => {
234
clearTimeout(timeoutId);
235
delegate.resolve(value);
236
},
237
error => {
238
clearTimeout(timeoutId);
239
delegate.reject(error);
240
}
241
);
242
243
timeoutId = setTimeout(() => {
244
delegate.reject(new Error(`Operation timed out after ${timeoutMs}ms`));
245
}, timeoutMs);
246
247
return delegate.promise;
248
}
249
250
// Usage
251
const slowOperation = new Promise(resolve => {
252
setTimeout(() => resolve("Done!"), 5000);
253
});
254
255
const fastTimeout = withTimeout(slowOperation, 2000);
256
257
fastTimeout.catch(error => {
258
console.log(error.message); // "Operation timed out after 2000ms"
259
});
260
```