@flex-development/when 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +820 -108
- package/dist/index.d.mts +526 -51
- package/dist/lib/index.mjs +5 -1
- package/dist/lib/is-catchable.mjs +24 -0
- package/dist/lib/is-finalizable.mjs +24 -0
- package/dist/lib/is-promise-like.mjs +24 -0
- package/dist/lib/is-promise.mjs +37 -0
- package/dist/lib/is-thenable.mjs +33 -9
- package/dist/lib/when.mjs +102 -19
- package/dist/testing/index.d.mts +149 -0
- package/dist/testing/index.mjs +5 -0
- package/dist/testing/lib/create-thenable.mjs +350 -0
- package/dist/testing/lib/index.mjs +5 -0
- package/package.json +25 -9
package/README.md
CHANGED
|
@@ -17,44 +17,94 @@ like `.then`, but for synchronous values *and* thenables.
|
|
|
17
17
|
## Contents
|
|
18
18
|
|
|
19
19
|
- [What is this?](#what-is-this)
|
|
20
|
+
- [When should I use this?](#when-should-i-use-this)
|
|
20
21
|
- [Why not `Promise.resolve`?](#why-not-promiseresolve)
|
|
22
|
+
- [Design guarantees](#design-guarantees)
|
|
21
23
|
- [Install](#install)
|
|
22
24
|
- [Use](#use)
|
|
23
25
|
- [Chain a synchronous value](#chain-a-synchronous-value)
|
|
24
26
|
- [Chain a *thenable*](#chain-a-thenable)
|
|
25
27
|
- [Pass arguments to the chain callback](#pass-arguments-to-the-chain-callback)
|
|
26
|
-
- [Handle
|
|
28
|
+
- [Handle failures](#handle-failures)
|
|
27
29
|
- [Bind `this` context](#bind-this-context)
|
|
28
30
|
- [Use an options object](#use-an-options-object)
|
|
31
|
+
- [Integrate with `@typescript-eslint`](#integrate-with-typescript-eslint)
|
|
29
32
|
- [API](#api)
|
|
33
|
+
- [`isCatchable<T>(value)`][iscatchable]
|
|
34
|
+
- [`isFinalizable<T>(value)`][isfinalizable]
|
|
35
|
+
- [`isPromise<T>(value[, finalizable])`][ispromise]
|
|
36
|
+
- [`isPromiseLike<T>(value)`][ispromiselike]
|
|
30
37
|
- [`isThenable<T>(value)`][isthenable]
|
|
31
|
-
|
|
38
|
+
<!--lint disable-->
|
|
39
|
+
- [`when<T[, Next][, Failure][, Args][, Error][, This][, Result]>(value, chain[, fail][, context][, ...args])`][when]
|
|
40
|
+
<!--lint enable-->
|
|
41
|
+
- [Testing](#testing)
|
|
42
|
+
- [`createThenable<T[, Reason][, Result]>(executor[, options])`][createthenable]
|
|
32
43
|
- [Types](#types)
|
|
33
44
|
- [`Awaitable<T>`][awaitable]
|
|
34
|
-
- [`
|
|
35
|
-
- [`
|
|
36
|
-
- [`
|
|
45
|
+
- [`Catch<[T][, Reason]>`][catch]
|
|
46
|
+
- [`Catchable<[T]>`][catchable]
|
|
47
|
+
- [`Chain<[T][, Next][, Args][, This]>`][chain]
|
|
48
|
+
- [`CreateThenableOptions`][createthenableoptions]
|
|
49
|
+
- [`Executor<[T][, Reason]>`][executor]
|
|
50
|
+
- [`Fail<[Next][, Reason][, This]>`][fail]
|
|
51
|
+
- [`Finalizable<[T]>`][finalizable]
|
|
52
|
+
- [`Finally<[T]>`][finally]
|
|
53
|
+
- [`Finish<[This]>`][finish]
|
|
54
|
+
- [`OnFinally`][onfinally]
|
|
55
|
+
- [`OnFulfilled<T[, Next]>`][onfulfilled]
|
|
56
|
+
- [`OnRejected<Next[, Reason]>`][onrejected]
|
|
57
|
+
- [`Options<[T][, Next][, Failure][, Args][, Error][, This]>`][options]
|
|
58
|
+
- [`PromiseLike<T>`][promiselike]
|
|
59
|
+
- [`Reject<[Reason]>`][reject]
|
|
60
|
+
- [`Resolve<[T]>`][resolve]
|
|
61
|
+
- [`Then<[T][, Reason]>`][then]
|
|
62
|
+
- [`Thenable<[T]>`][thenable]
|
|
37
63
|
- [Glossary](#glossary)
|
|
38
64
|
- [Project](#project)
|
|
39
65
|
- [Version](#version)
|
|
40
66
|
- [Contribute](#contribute)
|
|
67
|
+
- [Sponsor](#sponsor)
|
|
41
68
|
|
|
42
69
|
## What is this?
|
|
43
70
|
|
|
44
|
-
`when` is a
|
|
45
|
-
onto
|
|
71
|
+
`when` is a tiny primitive for chaining callbacks
|
|
72
|
+
onto [awaitables][awaitable] (synchronous or [*thenable*][thenable-term] values).
|
|
46
73
|
|
|
47
|
-
For thenable values,
|
|
48
|
-
|
|
49
|
-
This makes it easy to write one code path that supports both synchronous and asynchronous values.
|
|
74
|
+
For thenable values, `then` is used to invoke the callback after resolution. Otherwise, the callback fires immediately.
|
|
75
|
+
This makes it easy to write one code path that supports both synchronous values and promises.
|
|
50
76
|
|
|
51
|
-
|
|
52
|
-
loaders, or resolvers that may or may not return promises.
|
|
77
|
+
## When should I use this?
|
|
53
78
|
|
|
54
|
-
|
|
79
|
+
`when` is especially useful in libraries implementing awaitable APIs.
|
|
55
80
|
|
|
56
|
-
`
|
|
57
|
-
|
|
81
|
+
It provides [`Promise.prototype.then`][promise-then] semantics without forcing [`Promise.resolve`][promise-resolve],
|
|
82
|
+
preserving synchronous execution whenever possible.
|
|
83
|
+
|
|
84
|
+
Typical use cases include plugin systems, hook pipelines, module resolvers, data loaders, and file system adapters where
|
|
85
|
+
users may return both synchronous or asynchronous values.
|
|
86
|
+
|
|
87
|
+
If you're only dealing with promises and thenables, consider using native `async/await` or `Promise` chaining instead.
|
|
88
|
+
|
|
89
|
+
### Why not [`Promise.resolve`][promise-resolve]?
|
|
90
|
+
|
|
91
|
+
`when` preserves synchronous operations whenever possible,
|
|
92
|
+
avoiding unnecessary promise allocation and microtask scheduling.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
Promise.resolve(value).then(fn) // always a promise
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
when(value, fn) // only a promise if `value` is a thenable, or `fn` returns one
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Design guarantees
|
|
103
|
+
|
|
104
|
+
- Synchronous values remain synchronous
|
|
105
|
+
- [*Thenable*s][thenable-term] are chained directly without wrapping in [`Promise.resolve`][promise-resolve]
|
|
106
|
+
- No additional microtasks are scheduled for non-thenables
|
|
107
|
+
- Failures propagate unless a `fail` handler is provided
|
|
58
108
|
|
|
59
109
|
## Install
|
|
60
110
|
|
|
@@ -92,60 +142,71 @@ In browsers with [`esm.sh`][esmsh]:
|
|
|
92
142
|
### Chain a synchronous value
|
|
93
143
|
|
|
94
144
|
```ts
|
|
95
|
-
import { isThenable, when
|
|
145
|
+
import { isThenable, when } from '@flex-development/when'
|
|
96
146
|
import { ok } from 'devlop'
|
|
97
147
|
|
|
98
148
|
/**
|
|
99
149
|
* The result.
|
|
100
150
|
*
|
|
101
|
-
* @const {
|
|
151
|
+
* @const {number} result
|
|
102
152
|
*/
|
|
103
|
-
const result:
|
|
153
|
+
const result: number = when(0, n => n + 1)
|
|
104
154
|
|
|
105
155
|
ok(!isThenable(result), 'expected `result` to not be thenable')
|
|
106
156
|
console.dir(result) // 1
|
|
107
157
|
```
|
|
108
158
|
|
|
109
|
-
### Chain a [*thenable*][thenable]
|
|
159
|
+
### Chain a [*thenable*][thenable-term]
|
|
110
160
|
|
|
111
161
|
```ts
|
|
112
|
-
import {
|
|
162
|
+
import { isPromise, when } from '@flex-development/when'
|
|
113
163
|
import { ok } from 'devlop'
|
|
114
164
|
|
|
115
165
|
/**
|
|
116
166
|
* The result.
|
|
117
167
|
*
|
|
118
|
-
* @const {
|
|
168
|
+
* @const {Promise<number>} result
|
|
119
169
|
*/
|
|
120
|
-
const result:
|
|
170
|
+
const result: Promise<number> = when(Promise.resolve(2), n => n + 1)
|
|
121
171
|
|
|
122
|
-
ok(
|
|
172
|
+
ok(isPromise(result), 'expected `result` to be a promise')
|
|
123
173
|
console.dir(await result) // 3
|
|
124
174
|
```
|
|
125
175
|
|
|
126
176
|
### Pass arguments to the chain callback
|
|
127
177
|
|
|
128
|
-
|
|
178
|
+
When arguments are provided, they are passed to the [`chain`][chain] callback first, followed by the resolved value.
|
|
129
179
|
|
|
130
|
-
When the `value` passed to `when` is not [*thenable*][thenable], the resolved value is the same `value`.
|
|
180
|
+
When the `value` passed to `when` is not a [*thenable*][thenable-term], the resolved value is the same `value`.
|
|
131
181
|
|
|
132
182
|
```ts
|
|
133
|
-
import when
|
|
183
|
+
import when from '@flex-development/when'
|
|
134
184
|
|
|
135
185
|
/**
|
|
136
186
|
* The result.
|
|
137
187
|
*
|
|
138
|
-
* @const {
|
|
188
|
+
* @const {number} result
|
|
139
189
|
*/
|
|
140
|
-
const result:
|
|
190
|
+
const result: number = when(
|
|
191
|
+
1, // last argument passed to `Math.min`
|
|
192
|
+
Math.min, // `chain`
|
|
193
|
+
null, // `fail`
|
|
194
|
+
undefined, // `context`
|
|
195
|
+
2, // first argument passed to `Math.min`
|
|
196
|
+
3, // second argument passed to `Math.min`
|
|
197
|
+
4 // third argument passed to `Math.min`
|
|
198
|
+
)
|
|
141
199
|
|
|
142
200
|
console.dir(result) // 1
|
|
143
201
|
```
|
|
144
202
|
|
|
145
|
-
### Handle
|
|
203
|
+
### Handle failures
|
|
204
|
+
|
|
205
|
+
For [*thenable*s][thenable-term], the [`fail`][fail] callback is passed to [`then`][then] as the `onrejected` parameter,
|
|
206
|
+
and if implemented, to [`catch`][catch] as well to prevent unhandled rejections.
|
|
146
207
|
|
|
147
208
|
```ts
|
|
148
|
-
import when
|
|
209
|
+
import when from '@flex-development/when'
|
|
149
210
|
|
|
150
211
|
/**
|
|
151
212
|
* The thenable value.
|
|
@@ -159,9 +220,9 @@ const value: PromiseLike<never> = new Promise((resolve, reject) => {
|
|
|
159
220
|
/**
|
|
160
221
|
* The result.
|
|
161
222
|
*
|
|
162
|
-
* @const {
|
|
223
|
+
* @const {Promise<boolean>} result
|
|
163
224
|
*/
|
|
164
|
-
const result:
|
|
225
|
+
const result: Promise<boolean> = when(value, chain, fail)
|
|
165
226
|
|
|
166
227
|
console.dir(await result) // false
|
|
167
228
|
|
|
@@ -183,7 +244,7 @@ function chain(this: void): true {
|
|
|
183
244
|
* @return {false}
|
|
184
245
|
* The failure result
|
|
185
246
|
*/
|
|
186
|
-
function
|
|
247
|
+
function fail(this: void, e: Error): false {
|
|
187
248
|
return console.dir(e), false
|
|
188
249
|
}
|
|
189
250
|
```
|
|
@@ -191,7 +252,7 @@ function reject(this: void, e: Error): false {
|
|
|
191
252
|
### Bind `this` context
|
|
192
253
|
|
|
193
254
|
```ts
|
|
194
|
-
import when
|
|
255
|
+
import when from '@flex-development/when'
|
|
195
256
|
|
|
196
257
|
/**
|
|
197
258
|
* The `this` context.
|
|
@@ -201,9 +262,9 @@ type Context = { prefix: string }
|
|
|
201
262
|
/**
|
|
202
263
|
* The result.
|
|
203
264
|
*
|
|
204
|
-
* @const {
|
|
265
|
+
* @const {string} result
|
|
205
266
|
*/
|
|
206
|
-
const result:
|
|
267
|
+
const result: string = when(13, id, null, { prefix: 'id:' })
|
|
207
268
|
|
|
208
269
|
console.log(result) // 'id:13'
|
|
209
270
|
|
|
@@ -223,8 +284,6 @@ function id(this: Context, num: number | string): string {
|
|
|
223
284
|
### Use an options object
|
|
224
285
|
|
|
225
286
|
```ts
|
|
226
|
-
import when, { type Awaitable } from '@flex-development/when'
|
|
227
|
-
|
|
228
287
|
/**
|
|
229
288
|
* The `this` context.
|
|
230
289
|
*/
|
|
@@ -240,13 +299,13 @@ const value: Promise<number> = new Promise(resolve => resolve(3))
|
|
|
240
299
|
/**
|
|
241
300
|
* The result.
|
|
242
301
|
*
|
|
243
|
-
* @const {
|
|
302
|
+
* @const {Promise<number | undefined>} result
|
|
244
303
|
*/
|
|
245
|
-
const result:
|
|
304
|
+
const result: Promise<number | undefined> = when(value, {
|
|
246
305
|
args: [39],
|
|
247
306
|
chain: divide,
|
|
248
307
|
context: { errors: [] },
|
|
249
|
-
|
|
308
|
+
fail
|
|
250
309
|
})
|
|
251
310
|
|
|
252
311
|
console.dir(await result) // 13
|
|
@@ -272,22 +331,130 @@ function divide(this: void, dividend: number, divisor: number): number {
|
|
|
272
331
|
* The error to handle
|
|
273
332
|
* @return {undefined}
|
|
274
333
|
*/
|
|
275
|
-
function
|
|
334
|
+
function fail(this: Context, e: Error): undefined {
|
|
276
335
|
return void this.errors.push(e)
|
|
277
336
|
}
|
|
278
337
|
```
|
|
279
338
|
|
|
339
|
+
### Integrate with [`@typescript-eslint`][typescript-eslint]
|
|
340
|
+
|
|
341
|
+
```js
|
|
342
|
+
/**
|
|
343
|
+
* The eslint configuration.
|
|
344
|
+
*
|
|
345
|
+
* @type {import('eslint').Linter.Config[]}
|
|
346
|
+
* @const config
|
|
347
|
+
*/
|
|
348
|
+
const config = [
|
|
349
|
+
{
|
|
350
|
+
files: ['**/*.+(cjs|cts|js|jsx|mjs|mts|ts|tsx)'],
|
|
351
|
+
rules: {
|
|
352
|
+
'@typescript-eslint/promise-function-async': [
|
|
353
|
+
2,
|
|
354
|
+
{
|
|
355
|
+
allowedPromiseNames: ['Thenable']
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
export default config
|
|
363
|
+
```
|
|
364
|
+
|
|
280
365
|
## API
|
|
281
366
|
|
|
282
367
|
`when` exports the identifiers listed below.
|
|
283
368
|
|
|
284
369
|
The default export is [`when`][when].
|
|
285
370
|
|
|
286
|
-
### `
|
|
371
|
+
### `isCatchable<T>(value)`
|
|
372
|
+
|
|
373
|
+
Check if `value` looks like a [`Thenable`][thenable] that can be caught.
|
|
374
|
+
|
|
375
|
+
#### Type Parameters
|
|
376
|
+
|
|
377
|
+
- `T` (`any`)
|
|
378
|
+
— the resolved value
|
|
379
|
+
|
|
380
|
+
#### Parameters
|
|
381
|
+
|
|
382
|
+
- `value` (`unknown`)
|
|
383
|
+
— the thing to check
|
|
384
|
+
|
|
385
|
+
#### Returns
|
|
386
|
+
|
|
387
|
+
([`value is Catchable<T>`][catchable])
|
|
388
|
+
`true` if `value` is a [*thenable*][thenable-term] with a [`catch`][catch] method, `false` otherwise
|
|
389
|
+
|
|
390
|
+
### `isFinalizable<T>(value)`
|
|
391
|
+
|
|
392
|
+
Check if `value` looks like a [`Thenable`][thenable] that can be finalized.
|
|
393
|
+
|
|
394
|
+
#### Type Parameters
|
|
395
|
+
|
|
396
|
+
- `T` (`any`)
|
|
397
|
+
— the resolved value
|
|
398
|
+
|
|
399
|
+
#### Parameters
|
|
400
|
+
|
|
401
|
+
- `value` (`unknown`)
|
|
402
|
+
— the thing to check
|
|
403
|
+
|
|
404
|
+
#### Returns
|
|
405
|
+
|
|
406
|
+
([`value is Finalizable<T>`][finalizable])
|
|
407
|
+
`true` if `value` is a [*thenable*][thenable-term] with a [`finally`][finally] method, `false` otherwise
|
|
408
|
+
|
|
409
|
+
### `isPromise<T>(value[, finalizable])`
|
|
410
|
+
|
|
411
|
+
Check if `value` looks like a `Promise`.
|
|
412
|
+
|
|
413
|
+
> 👉 **Note**: This function intentionally performs structural checks instead of brand checks.
|
|
414
|
+
> It does not rely on `instanceof Promise` or constructors, making it compatible with cross-realm
|
|
415
|
+
> promises and custom thenables.
|
|
416
|
+
|
|
417
|
+
#### Type Parameters
|
|
418
|
+
|
|
419
|
+
- `T` (`any`)
|
|
420
|
+
— the resolved value
|
|
421
|
+
|
|
422
|
+
#### Parameters
|
|
423
|
+
|
|
424
|
+
- `value` (`unknown`)
|
|
425
|
+
— the thing to check
|
|
426
|
+
- `finalizable` (`boolean` | `null` | `undefined`)
|
|
427
|
+
— whether a [`finally`][finally] method is required.\
|
|
428
|
+
when `false`, only [`then`][then] and [`catch`][catch] are checked
|
|
429
|
+
|
|
430
|
+
#### Returns
|
|
431
|
+
|
|
432
|
+
(`value is Promise<T>`)
|
|
433
|
+
`true` if `value` is a [*thenable*][thenable-term] with a [`catch`][catch] method,
|
|
434
|
+
and [`finally`][finally] method (if requested), `false` otherwise
|
|
435
|
+
|
|
436
|
+
### `isPromiseLike<T>(value)`
|
|
437
|
+
|
|
438
|
+
Check if `value` looks like a [`PromiseLike`][promiselike] structure.
|
|
439
|
+
|
|
440
|
+
#### Type Parameters
|
|
441
|
+
|
|
442
|
+
- `T` (`any`)
|
|
443
|
+
— the resolved value
|
|
444
|
+
|
|
445
|
+
#### Parameters
|
|
446
|
+
|
|
447
|
+
- `value` (`unknown`)
|
|
448
|
+
— the thing to check
|
|
287
449
|
|
|
288
|
-
|
|
450
|
+
#### Returns
|
|
451
|
+
|
|
452
|
+
([`value is PromiseLike<T>`][promiselike])
|
|
453
|
+
`true` if `value` is an object or function with a [`then`][then] method, `false` otherwise
|
|
454
|
+
|
|
455
|
+
### `isThenable<T>(value)`
|
|
289
456
|
|
|
290
|
-
|
|
457
|
+
Check if `value` looks like a [*thenable*][thenable-term].
|
|
291
458
|
|
|
292
459
|
#### Type Parameters
|
|
293
460
|
|
|
@@ -301,15 +468,17 @@ Check if `value` looks like a [*thenable*][thenable].
|
|
|
301
468
|
|
|
302
469
|
#### Returns
|
|
303
470
|
|
|
304
|
-
(`value is
|
|
471
|
+
([`value is Thenable<T>`][thenable]) `true` if `value` is an object or function with a [`then`][then] method,
|
|
472
|
+
and maybe-callable methods [`catch`][catch] and/or [`finally`][finally], `false` otherwise
|
|
305
473
|
|
|
306
474
|
<!--lint disable-->
|
|
307
475
|
|
|
308
|
-
### `when<
|
|
476
|
+
### `when<T[, Next][, Failure][, Args][, Error][, This][, Result]>(value, chain[, fail][, context][, ...args])`
|
|
309
477
|
|
|
310
478
|
<!--lint enable-->
|
|
311
479
|
|
|
312
|
-
Chain a callback, calling the function after `value` is resolved,
|
|
480
|
+
Chain a callback, calling the function after `value` is resolved,
|
|
481
|
+
or immediately if `value` is not a [*thenable*][thenable-term].
|
|
313
482
|
|
|
314
483
|
#### Overloads
|
|
315
484
|
|
|
@@ -318,28 +487,51 @@ function when<
|
|
|
318
487
|
T,
|
|
319
488
|
Next = any,
|
|
320
489
|
Args extends any[] = any[],
|
|
321
|
-
|
|
490
|
+
This = unknown,
|
|
491
|
+
Result extends Awaitable<Next> = Awaitable<Next>
|
|
492
|
+
>(
|
|
493
|
+
this: void,
|
|
494
|
+
value: Awaitable<T>,
|
|
495
|
+
chain: Chain<T, Next, Args, This>,
|
|
496
|
+
fail?: null | undefined,
|
|
497
|
+
context?: This | null | undefined,
|
|
498
|
+
...args: Args
|
|
499
|
+
): Result
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
function when<
|
|
504
|
+
T,
|
|
505
|
+
Next = any,
|
|
506
|
+
Failure = Next,
|
|
507
|
+
Args extends any[] = any[],
|
|
508
|
+
Error = any,
|
|
509
|
+
This = unknown,
|
|
510
|
+
Result extends Awaitable<Failure | Next> = Awaitable<Failure | Next>
|
|
322
511
|
>(
|
|
323
512
|
this: void,
|
|
324
513
|
value: Awaitable<T>,
|
|
325
|
-
chain: Chain<T, Next, Args,
|
|
326
|
-
|
|
327
|
-
context?:
|
|
514
|
+
chain: Chain<T, Next, Args, This>,
|
|
515
|
+
fail?: Fail<Failure, Error, This> | null | undefined,
|
|
516
|
+
context?: This | null | undefined,
|
|
328
517
|
...args: Args
|
|
329
|
-
):
|
|
518
|
+
): Result
|
|
330
519
|
```
|
|
331
520
|
|
|
332
521
|
```ts
|
|
333
522
|
function when<
|
|
334
523
|
T,
|
|
335
524
|
Next = any,
|
|
525
|
+
Failure = Next,
|
|
336
526
|
Args extends any[] = any[],
|
|
337
|
-
|
|
527
|
+
Error = any,
|
|
528
|
+
This = unknown,
|
|
529
|
+
Result extends Awaitable<Failure | Next> = Awaitable<Failure | Next>
|
|
338
530
|
>(
|
|
339
531
|
this: void,
|
|
340
532
|
value: Awaitable<T>,
|
|
341
|
-
chain: Options<T, Next, Args,
|
|
342
|
-
):
|
|
533
|
+
chain: Options<T, Next, Failure, Args, Error, This>
|
|
534
|
+
): Result
|
|
343
535
|
```
|
|
344
536
|
|
|
345
537
|
#### Type Parameters
|
|
@@ -349,29 +541,106 @@ function when<
|
|
|
349
541
|
- `Next` (`any`, optional)
|
|
350
542
|
— the next resolved value
|
|
351
543
|
- **default**: `any`
|
|
544
|
+
- `Failure` (`any`, optional)
|
|
545
|
+
— the next resolved value on failure
|
|
546
|
+
- **default**: `Next`
|
|
352
547
|
- `Args` (`readonly any[]`, optional)
|
|
353
|
-
— the function arguments
|
|
548
|
+
— the chain function arguments
|
|
354
549
|
- **default**: `any[]`
|
|
355
|
-
- `
|
|
550
|
+
- `Error` (`any`, optional)
|
|
551
|
+
— the error to possibly handle
|
|
552
|
+
- **default**: `any`
|
|
553
|
+
- `This` (`any`, optional)
|
|
356
554
|
— the `this` context
|
|
357
555
|
- **default**: `unknown`
|
|
556
|
+
- `Result` ([`Awaitable<Failure | Next>`][awaitable], optional)
|
|
557
|
+
— the next awaitable
|
|
558
|
+
- **default**: [`Awaitable<Failure | Next>`][awaitable]
|
|
358
559
|
|
|
359
560
|
#### Parameters
|
|
360
561
|
|
|
361
562
|
- `value` ([`Awaitable<T>`][awaitable])
|
|
362
|
-
— the
|
|
363
|
-
- `chain` ([`Chain<T, Next, Args,
|
|
563
|
+
— the current [*awaitable*][awaitable-term]
|
|
564
|
+
- `chain` ([`Chain<T, Next, Args, This>`][chain] | [`Options<T, Next, Failure, Args, Error, This>`][options])
|
|
364
565
|
— the chain callback or options for chaining
|
|
365
|
-
- `
|
|
366
|
-
— the callback to fire when a
|
|
367
|
-
-
|
|
368
|
-
|
|
566
|
+
- `fail` ([`Fail<Failure, Error, This>`][fail] | `null` | `undefined`)
|
|
567
|
+
— the callback to fire when a failure occurs. failures include:
|
|
568
|
+
- rejections of the input [*thenable*][thenable-term]
|
|
569
|
+
- rejections returned from `chain`
|
|
570
|
+
- synchronous errors thrown in `chain`\
|
|
571
|
+
if no `fail` handler is provided, failures are re-thrown or re-propagated.
|
|
572
|
+
> 👉 **note**: for thenables, this callback is passed to `then` as the `onrejected` parameter,
|
|
573
|
+
> and if implemented, to `catch` as well to prevent unhandled rejections.
|
|
574
|
+
- `context` (`This` | `null` | `undefined`)
|
|
575
|
+
— the `this` context of the chain and `fail` callbacks
|
|
369
576
|
- `...args` (`Args`)
|
|
370
577
|
— the arguments to pass to the chain callback
|
|
371
578
|
|
|
372
579
|
#### Returns
|
|
373
580
|
|
|
374
|
-
([`Awaitable<Next>`][awaitable]) The next
|
|
581
|
+
([`Awaitable<Failure | Next>`][awaitable] | [`Awaitable<Next>`][awaitable]) The next [*awaitable*][awaitable-term]
|
|
582
|
+
|
|
583
|
+
## Testing
|
|
584
|
+
|
|
585
|
+
Test utilities are exported from `@flex-development/when/testing`.
|
|
586
|
+
|
|
587
|
+
There is no default export.
|
|
588
|
+
|
|
589
|
+
```ts
|
|
590
|
+
import {
|
|
591
|
+
isCatchable,
|
|
592
|
+
isFinalizable,
|
|
593
|
+
type Thenable
|
|
594
|
+
} from '@flex-development/when'
|
|
595
|
+
import { createThenable } from '@flex-development/when/testing'
|
|
596
|
+
import { ok } from 'devlop'
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* The thenable.
|
|
600
|
+
*
|
|
601
|
+
* @const {Thenable<number>} thenable
|
|
602
|
+
*/
|
|
603
|
+
const thenable: Thenable<number> = createThenable(resolve => resolve(10))
|
|
604
|
+
|
|
605
|
+
ok(isCatchable(thenable), 'expected `thenable` to be a catchable')
|
|
606
|
+
ok(isFinalizable(thenable), 'expected `thenable` to be a finalizable')
|
|
607
|
+
|
|
608
|
+
console.dir(await thenable.then(value => value + 3)) // 13
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### `createThenable<T[, Reason][, Result]>(executor[, options])`
|
|
612
|
+
|
|
613
|
+
Create a thenable.
|
|
614
|
+
|
|
615
|
+
The returned object conforms to [`Thenable`][thenable] and ensures [`then`][then] always returns another `Thenable`,
|
|
616
|
+
even when adopting a foreign [*thenable*][thenable-term].
|
|
617
|
+
|
|
618
|
+
When `options` is omitted, `null`, or `undefined`, the returned thenable is *modern* (a [*thenable*][thenable-term]
|
|
619
|
+
with [`then`][then], [`catch`][catch], and [`finally`][finally] methods).
|
|
620
|
+
Pass an options object (e.g. `{}`) to start from a *bare* ([`then`][then] method only) thenable
|
|
621
|
+
and selectively enable methods.
|
|
622
|
+
|
|
623
|
+
#### Type Parameters
|
|
624
|
+
|
|
625
|
+
- `T` (`any`)
|
|
626
|
+
— the resolved value
|
|
627
|
+
- `Reason` (`any`, optional)
|
|
628
|
+
— the reason for a rejection
|
|
629
|
+
- **default**: `Error`
|
|
630
|
+
- `Result` ([`Thenable<T>`][thenable], optional)
|
|
631
|
+
— the thenable
|
|
632
|
+
- **default**: [`Thenable<T>`][thenable]
|
|
633
|
+
|
|
634
|
+
#### Parameters
|
|
635
|
+
|
|
636
|
+
- `executor` ([`Executor<T, Reason>`][executor])
|
|
637
|
+
— the initialization callback
|
|
638
|
+
- `options` ([`CreateThenableOptions`][createthenableoptions] | `null` | `undefined`, optional)
|
|
639
|
+
— options for creating a thenable
|
|
640
|
+
|
|
641
|
+
#### Returns
|
|
642
|
+
|
|
643
|
+
(`Result`) The [*thenable*][thenable-term]
|
|
375
644
|
|
|
376
645
|
## Types
|
|
377
646
|
|
|
@@ -379,10 +648,10 @@ This package is fully typed with [TypeScript][].
|
|
|
379
648
|
|
|
380
649
|
### `Awaitable<T>`
|
|
381
650
|
|
|
382
|
-
A synchronous or [*thenable*][thenable] value (`type`).
|
|
651
|
+
A synchronous or [*thenable*][thenable-term] value (`type`).
|
|
383
652
|
|
|
384
653
|
```ts
|
|
385
|
-
type Awaitable<T> =
|
|
654
|
+
type Awaitable<T> = Thenable<T> | T
|
|
386
655
|
```
|
|
387
656
|
|
|
388
657
|
#### Type Parameters
|
|
@@ -390,7 +659,58 @@ type Awaitable<T> = PromiseLike<T> | T
|
|
|
390
659
|
- `T` (`any`)
|
|
391
660
|
— the resolved value
|
|
392
661
|
|
|
393
|
-
### `
|
|
662
|
+
### `Catch<[T][, Reason]>`
|
|
663
|
+
|
|
664
|
+
Attach a callback only for the rejection of a [`Thenable`][thenable] (`type`).
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
type Catch<T = unknown, Reason = any> = <Next = never>(
|
|
668
|
+
this: any,
|
|
669
|
+
onrejected?: OnRejected<Next, Reason> | null | undefined
|
|
670
|
+
) => Thenable<Next | T>
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
#### Type Parameters
|
|
674
|
+
|
|
675
|
+
- `T` (`any`, optional)
|
|
676
|
+
— the resolved value
|
|
677
|
+
- **default**: `unknown`
|
|
678
|
+
- `Reason` (`any`, optional)
|
|
679
|
+
— the reason for the rejection
|
|
680
|
+
- **default**: `any`
|
|
681
|
+
- `Next` (`any`, optional)
|
|
682
|
+
— the next resolved value
|
|
683
|
+
- **default**: `never`
|
|
684
|
+
|
|
685
|
+
#### Parameters
|
|
686
|
+
|
|
687
|
+
- `onrejected` ([`OnRejected<Next, Reason>`][onrejected] | `null` | `undefined`)
|
|
688
|
+
— the callback to execute when the thenable is rejected
|
|
689
|
+
|
|
690
|
+
#### Returns
|
|
691
|
+
|
|
692
|
+
([`Thenable<Next | T>`][thenable]) The next [*thenable*][thenable-term]
|
|
693
|
+
|
|
694
|
+
### `Catchable<[T]>`
|
|
695
|
+
|
|
696
|
+
A [`Thenable`][thenable] that can be caught (`interface`).
|
|
697
|
+
|
|
698
|
+
#### Extends
|
|
699
|
+
|
|
700
|
+
- [`Thenable<T>`][thenable]
|
|
701
|
+
|
|
702
|
+
#### Type Parameters
|
|
703
|
+
|
|
704
|
+
- `T` (`any`, optional)
|
|
705
|
+
— the resolved value
|
|
706
|
+
- **default**: `any`
|
|
707
|
+
|
|
708
|
+
#### Properties
|
|
709
|
+
|
|
710
|
+
- `catch` ([`Catch<T>`][catch])
|
|
711
|
+
— attach a callback only to be invoked on rejection
|
|
712
|
+
|
|
713
|
+
### `Chain<[T][, Next][, Args][, This]>`
|
|
394
714
|
|
|
395
715
|
A chain callback (`type`).
|
|
396
716
|
|
|
@@ -399,8 +719,8 @@ type Chain<
|
|
|
399
719
|
T = any,
|
|
400
720
|
Next = any,
|
|
401
721
|
Args extends readonly any[] = any[],
|
|
402
|
-
|
|
403
|
-
> = (this:
|
|
722
|
+
This = unknown
|
|
723
|
+
> = (this: This, ...params: [...Args, T]) => Awaitable<Next>
|
|
404
724
|
```
|
|
405
725
|
|
|
406
726
|
#### Type Parameters
|
|
@@ -414,22 +734,244 @@ type Chain<
|
|
|
414
734
|
- `Args` (`readonly any[]`, optional)
|
|
415
735
|
— the function arguments
|
|
416
736
|
- **default**: `any[]`
|
|
417
|
-
- `
|
|
737
|
+
- `This` (`any`, optional)
|
|
418
738
|
— the `this` context
|
|
419
739
|
- **default**: `unknown`
|
|
420
740
|
|
|
421
741
|
#### Parameters
|
|
422
742
|
|
|
423
|
-
- **`this`** (`
|
|
743
|
+
- **`this`** (`This`)
|
|
424
744
|
- `...params` (`[...Args, T]`)
|
|
425
745
|
— the function parameters, with the last being the previously resolved value.
|
|
426
746
|
in cases where a promise is not being resolved, this is the same `value` passed to `when`
|
|
427
747
|
|
|
428
748
|
#### Returns
|
|
429
749
|
|
|
430
|
-
([`Awaitable<Next>`][awaitable]) The next
|
|
750
|
+
([`Awaitable<Next>`][awaitable]) The next [*awaitable*][awaitable-term]
|
|
751
|
+
|
|
752
|
+
### `CreateThenableOptions`
|
|
753
|
+
|
|
754
|
+
Options for creating a [*thenable*][thenable-term] (`interface`).
|
|
755
|
+
|
|
756
|
+
> 👉 **Note**: Exported from `@flex-development/when/testing` only.
|
|
757
|
+
|
|
758
|
+
#### Properties
|
|
759
|
+
|
|
760
|
+
- `catch?` (`boolean` | `null` | `undefined`)
|
|
761
|
+
— control whether returned thenables implement a [`catch`][catch] method.\
|
|
762
|
+
when an options object is omitted, `null`, or `undefined`, the method will be implemented.\<br/>
|
|
763
|
+
when an options object is provided, `catch` is only implemented if `options.catch` is `true`.\
|
|
764
|
+
if `options.catch` is `null` or `undefined`, the thenable's `catch` property will have the same value.\
|
|
765
|
+
pass `false` to disable the method implementation
|
|
766
|
+
- `finally?` (`boolean` | `null` | `undefined`)
|
|
767
|
+
— control whether returned thenables implement a [`finally`][finally] method.\
|
|
768
|
+
when an options object is omitted, `null`, or `undefined`, the method will be implemented.\
|
|
769
|
+
when an options object is provided, `finally` is only implemented if `options.finally` is `true`.\
|
|
770
|
+
if `options.finally` is `null` or `undefined`, the thenable's `finally` property will have the same value.\
|
|
771
|
+
pass `false` to disable the method implementation
|
|
772
|
+
|
|
773
|
+
### `Executor<[T][, Reason]>`
|
|
774
|
+
|
|
775
|
+
The callback used to initialize a [*thenable*][thenable-term] (`type`).
|
|
431
776
|
|
|
432
|
-
|
|
777
|
+
> 👉 **Note**: Exported from `@flex-development/when/testing` only.
|
|
778
|
+
|
|
779
|
+
```ts
|
|
780
|
+
type Executor<T = any, Reason = Error> = (
|
|
781
|
+
this: void,
|
|
782
|
+
resolve: Resolve<T>,
|
|
783
|
+
reject: Reject<Reason>
|
|
784
|
+
) => undefined | void
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
#### Type Parameters
|
|
788
|
+
|
|
789
|
+
- `T` (`any`, optional)
|
|
790
|
+
— the resolved value
|
|
791
|
+
- **default**: `any`
|
|
792
|
+
- `Reason` (`any`, optional)
|
|
793
|
+
— the reason for a rejection
|
|
794
|
+
- **default**: `Error`
|
|
795
|
+
|
|
796
|
+
#### Parameters
|
|
797
|
+
|
|
798
|
+
- `resolve` ([`Resolve<T>`][resolve])
|
|
799
|
+
— the callback used to resolve the thenable with a value or the result of another [*awaitable*][awaitable-term]
|
|
800
|
+
- `reject` ([`Reject<Reason>`][reject])
|
|
801
|
+
— the callback used to reject the thenable with a provided reason or error
|
|
802
|
+
|
|
803
|
+
#### Returns
|
|
804
|
+
|
|
805
|
+
(`undefined` | `void`) Nothing
|
|
806
|
+
|
|
807
|
+
### `Fail<[Next][, Reason][, This]>`
|
|
808
|
+
|
|
809
|
+
The callback to fire when a failure occurs (`type`).
|
|
810
|
+
|
|
811
|
+
```ts
|
|
812
|
+
type Fail<
|
|
813
|
+
Next = any,
|
|
814
|
+
Reason = any,
|
|
815
|
+
This = unknown
|
|
816
|
+
> = (this: This, reason: Reason) => Awaitable<Next>
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
#### Type Parameters
|
|
820
|
+
|
|
821
|
+
- `Next` (`any`, optional)
|
|
822
|
+
— the next resolved value
|
|
823
|
+
- **default**: `any`
|
|
824
|
+
- `Reason` (`any`, optional)
|
|
825
|
+
— the reason for the failure
|
|
826
|
+
- **default**: `any`
|
|
827
|
+
- `This` (`any`, optional)
|
|
828
|
+
— the `this` context
|
|
829
|
+
- **default**: `unknown`
|
|
830
|
+
|
|
831
|
+
#### Parameters
|
|
832
|
+
|
|
833
|
+
- **`this`** (`This`)
|
|
834
|
+
- `reason` (`Reason`)
|
|
835
|
+
— the reason for the failure
|
|
836
|
+
|
|
837
|
+
#### Returns
|
|
838
|
+
|
|
839
|
+
([`Awaitable<Next>`][awaitable]) The next [*awaitable*][awaitable-term]
|
|
840
|
+
|
|
841
|
+
### `Finalizable<[T]>`
|
|
842
|
+
|
|
843
|
+
A [`Thenable`][thenable] that can be finalized (`interface`).
|
|
844
|
+
|
|
845
|
+
#### Extends
|
|
846
|
+
|
|
847
|
+
- [`Thenable<T>`][thenable]
|
|
848
|
+
|
|
849
|
+
#### Type Parameters
|
|
850
|
+
|
|
851
|
+
- `T` (`any`, optional)
|
|
852
|
+
— the resolved value
|
|
853
|
+
- **default**: `any`
|
|
854
|
+
|
|
855
|
+
#### Properties
|
|
856
|
+
|
|
857
|
+
- `finally` ([`Finally<T>`][finally])
|
|
858
|
+
— attach a callback only to be invoked on settlement (fulfillment or rejection)
|
|
859
|
+
> 👉 **note**: the resolved value cannot be modified from the callback
|
|
860
|
+
|
|
861
|
+
### `Finally<[T]>`
|
|
862
|
+
|
|
863
|
+
Attach a callback that is invoked only when a [`Thenable`][thenable] is settled (fulfilled or rejected) (`type`).
|
|
864
|
+
|
|
865
|
+
```ts
|
|
866
|
+
type Finally<T = unknown> = (
|
|
867
|
+
this: any,
|
|
868
|
+
onfinally?: OnFinally | null | undefined
|
|
869
|
+
) => Thenable<T>
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### Type Parameters
|
|
873
|
+
|
|
874
|
+
- `T` (`any`, optional)
|
|
875
|
+
— the resolved value
|
|
876
|
+
- **default**: `unknown`
|
|
877
|
+
|
|
878
|
+
#### Parameters
|
|
879
|
+
|
|
880
|
+
- `onfinally` ([`OnFinally`][onfinally] | `null` | `undefined`)
|
|
881
|
+
— the callback to execute when the thenable is settled
|
|
882
|
+
|
|
883
|
+
#### Returns
|
|
884
|
+
|
|
885
|
+
([`Thenable<T>`][thenable]) The next [*thenable*][thenable-term]
|
|
886
|
+
|
|
887
|
+
### `Finish<[This]>`
|
|
888
|
+
|
|
889
|
+
A post-processing hook invoked exactly once after an [*awaitable*][awaitable-term] settles,
|
|
890
|
+
regardless of success or failure (`type`).
|
|
891
|
+
|
|
892
|
+
The resolved value cannot be modified from the hook, and any error is re-thrown after execution.
|
|
893
|
+
|
|
894
|
+
```ts
|
|
895
|
+
type Finish<This = unknown> = (this: This) => undefined | void
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
#### Type Parameters
|
|
899
|
+
|
|
900
|
+
- `This` (`any`, optional)
|
|
901
|
+
— the `this` context
|
|
902
|
+
- **default**: `unknown`
|
|
903
|
+
|
|
904
|
+
#### Returns
|
|
905
|
+
|
|
906
|
+
(`undefined` | `void`) Nothing
|
|
907
|
+
|
|
908
|
+
### `OnFinally`
|
|
909
|
+
|
|
910
|
+
The callback to execute when a [`Thenable`][thenable] is settled (fulfilled or rejected) (`type`).
|
|
911
|
+
|
|
912
|
+
```ts
|
|
913
|
+
type OnFinally = (this: unknown) => undefined | void
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
#### Returns
|
|
917
|
+
|
|
918
|
+
(`undefined` | `void`) Nothing
|
|
919
|
+
|
|
920
|
+
### `OnFulfilled<T[, Next]>`
|
|
921
|
+
|
|
922
|
+
The callback to execute when a [`Thenable`][thenable] is resolved (`type`).
|
|
923
|
+
|
|
924
|
+
```ts
|
|
925
|
+
type OnFulfilled<T, Next = T> = (this: unknown, value: T) => Awaitable<Next>
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
#### Type Parameters
|
|
929
|
+
|
|
930
|
+
- `T` (`any`)
|
|
931
|
+
— the resolved value
|
|
932
|
+
- `Next` (`any`, optional)
|
|
933
|
+
— the next resolved value
|
|
934
|
+
- **default**: `T`
|
|
935
|
+
|
|
936
|
+
#### Parameters
|
|
937
|
+
|
|
938
|
+
- `value` (`T`)
|
|
939
|
+
— the resolved value
|
|
940
|
+
|
|
941
|
+
#### Returns
|
|
942
|
+
|
|
943
|
+
([`Awaitable<Next>`][awaitable]) The next [*awaitable*][awaitable-term]
|
|
944
|
+
|
|
945
|
+
### `OnRejected<Next[, Reason]>`
|
|
946
|
+
|
|
947
|
+
The callback to execute when a [`Thenable`][thenable] is rejected (`type`).
|
|
948
|
+
|
|
949
|
+
```ts
|
|
950
|
+
type OnRejected<
|
|
951
|
+
Next,
|
|
952
|
+
Reason = any
|
|
953
|
+
> = (this: unknown, reason: Reason) => Awaitable<Next>
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
#### Type Parameters
|
|
957
|
+
|
|
958
|
+
- `Next` (`any`, optional)
|
|
959
|
+
— the next resolved value
|
|
960
|
+
- **default**: `any`
|
|
961
|
+
- `Reason` (`any`, optional)
|
|
962
|
+
— the reason for the rejection
|
|
963
|
+
- **default**: `any`
|
|
964
|
+
|
|
965
|
+
#### Parameters
|
|
966
|
+
|
|
967
|
+
- `reason` (`Reason`)
|
|
968
|
+
— the reason for the rejection
|
|
969
|
+
|
|
970
|
+
#### Returns
|
|
971
|
+
|
|
972
|
+
([`Awaitable<Next>`][awaitable]) The next [*awaitable*][awaitable-term]
|
|
973
|
+
|
|
974
|
+
### `Options<[T][, Next][, Failure][, Args][, Error][, This]>`
|
|
433
975
|
|
|
434
976
|
Options for chaining (`interface`).
|
|
435
977
|
|
|
@@ -437,8 +979,10 @@ Options for chaining (`interface`).
|
|
|
437
979
|
interface Options<
|
|
438
980
|
T = any,
|
|
439
981
|
Next = any,
|
|
982
|
+
Failure = Next,
|
|
440
983
|
Args extends readonly any[] = any[],
|
|
441
|
-
|
|
984
|
+
Error = any,
|
|
985
|
+
This = any
|
|
442
986
|
> { /* ... */ }
|
|
443
987
|
```
|
|
444
988
|
|
|
@@ -450,10 +994,16 @@ interface Options<
|
|
|
450
994
|
- `Next` (`any`, optional)
|
|
451
995
|
— the next resolved value
|
|
452
996
|
- **default**: `any`
|
|
997
|
+
- `Failure` (`any`, optional)
|
|
998
|
+
— the next resolved value on failure
|
|
999
|
+
- **default**: `Next`
|
|
453
1000
|
- `Args` (`readonly any[]`, optional)
|
|
454
1001
|
— the chain function arguments
|
|
455
1002
|
- **default**: `any[]`
|
|
456
|
-
- `
|
|
1003
|
+
- `Error` (`any`, optional)
|
|
1004
|
+
— the error to possibly handle
|
|
1005
|
+
- **default**: `any`
|
|
1006
|
+
- `This` (`any`, optional)
|
|
457
1007
|
— the `this` context
|
|
458
1008
|
- **default**: `any`
|
|
459
1009
|
|
|
@@ -461,60 +1011,169 @@ interface Options<
|
|
|
461
1011
|
|
|
462
1012
|
- `args?` (`Args` | `null` | `undefined`)
|
|
463
1013
|
— the arguments to pass to the `chain` callback
|
|
464
|
-
- `chain` ([`Chain<T, Next, Args,
|
|
1014
|
+
- `chain` ([`Chain<T, Next, Args, This>`][chain])
|
|
465
1015
|
— the chain callback
|
|
466
|
-
- `context?` (`
|
|
467
|
-
— the `this` context of the `chain` and `
|
|
468
|
-
- `
|
|
469
|
-
— the callback to fire when a
|
|
1016
|
+
- `context?` (`This` | `null` | `undefined`)
|
|
1017
|
+
— the `this` context of the `chain` and `fail` callbacks
|
|
1018
|
+
- `fail?` ([`Fail<Next, Error, This>`][fail] | `null` | `undefined`)
|
|
1019
|
+
— the callback to fire when a failure occurs. failures include:
|
|
1020
|
+
- rejections of the input [*thenable*][thenable-term]
|
|
1021
|
+
- rejections returned from `chain`
|
|
1022
|
+
- synchronous errors thrown in `chain`\
|
|
1023
|
+
if no `fail` handler is provided, failures are re-thrown or re-propagated.
|
|
1024
|
+
> 👉 **note**: for thenables, this callback is passed to `then` as the `onrejected` parameter,
|
|
1025
|
+
> and if implemented, to [`catch`][catch] as well to prevent unhandled rejections.
|
|
1026
|
+
- `finish?` ([`Finish<This>`][finish] | `null` | `undefined`)
|
|
1027
|
+
— the callback to invoke after chaining completes, whether the operation succeeds or fails.\
|
|
1028
|
+
it runs exactly once after `chain` and `fail`, cannot affect the resolved value, and does not intercept errors
|
|
1029
|
+
|
|
1030
|
+
### `PromiseLike<T>`
|
|
1031
|
+
|
|
1032
|
+
To ensure native `Promise` and `PromiseLike` are assignable to [`Thenable`][thenable],
|
|
1033
|
+
`when` ships a small global augmentation for `PromiseLike`.
|
|
1034
|
+
|
|
1035
|
+
No new methods or overloads are introduced — the `then` signature is rewritten to match
|
|
1036
|
+
the official [TypeScript][] lib definition (as in `lib.es2015.d.ts`).
|
|
1037
|
+
|
|
1038
|
+
This is required for both compatibility, and type inference when mixing `Thenable` with built-in promise types.
|
|
1039
|
+
|
|
1040
|
+
#### Type Parameters
|
|
1041
|
+
|
|
1042
|
+
- `T` (`any`)
|
|
1043
|
+
— the resolved value
|
|
470
1044
|
|
|
471
|
-
### `Reject<[
|
|
1045
|
+
### `Reject<[Reason]>`
|
|
472
1046
|
|
|
473
|
-
The callback to
|
|
1047
|
+
The callback used to reject a [*thenable*][thenable-term] with a provided reason or error (`type`).
|
|
1048
|
+
|
|
1049
|
+
> 👉 **Note**: Exported from `@flex-development/when/testing` only.
|
|
474
1050
|
|
|
475
1051
|
```ts
|
|
476
|
-
type Reject<
|
|
477
|
-
Next = any,
|
|
478
|
-
Fail = any,
|
|
479
|
-
Self = unknown
|
|
480
|
-
> = (this: Self, e: Fail) => Awaitable<Next>
|
|
1052
|
+
type Reject<Reason = Error> = (this: void, reason: Reason) => undefined
|
|
481
1053
|
```
|
|
482
1054
|
|
|
483
1055
|
#### Type Parameters
|
|
484
1056
|
|
|
485
|
-
- `
|
|
486
|
-
— the
|
|
487
|
-
- **default**: `
|
|
488
|
-
|
|
489
|
-
|
|
1057
|
+
- `Reason` (`any`, optional)
|
|
1058
|
+
— the reason for the rejection
|
|
1059
|
+
- **default**: `Error`
|
|
1060
|
+
|
|
1061
|
+
#### Parameters
|
|
1062
|
+
|
|
1063
|
+
- `reason` (`Reason`)
|
|
1064
|
+
— the reason for the rejection
|
|
1065
|
+
|
|
1066
|
+
#### Returns
|
|
1067
|
+
|
|
1068
|
+
(`undefined`) Nothing
|
|
1069
|
+
|
|
1070
|
+
### `Resolve<[T]>`
|
|
1071
|
+
|
|
1072
|
+
The callback used to resolve a [*thenable*][thenable-term] with a value
|
|
1073
|
+
or the result of another [*awaitable*][awaitable-term] (`type`).
|
|
1074
|
+
|
|
1075
|
+
> 👉 **Note**: Exported from `@flex-development/when/testing` only.
|
|
1076
|
+
|
|
1077
|
+
```ts
|
|
1078
|
+
type Resolve<T = any> = (this: void, value: Awaitable<T>) => undefined
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
#### Type Parameters
|
|
1082
|
+
|
|
1083
|
+
- `T` (`any`, optional)
|
|
1084
|
+
— the resolved value
|
|
490
1085
|
- **default**: `any`
|
|
491
|
-
|
|
492
|
-
|
|
1086
|
+
|
|
1087
|
+
#### Parameters
|
|
1088
|
+
|
|
1089
|
+
- `value` ([`Awaitable<T>`][awaitable])
|
|
1090
|
+
— the awaitable
|
|
1091
|
+
|
|
1092
|
+
#### Returns
|
|
1093
|
+
|
|
1094
|
+
(`undefined`) Nothing
|
|
1095
|
+
|
|
1096
|
+
### `Then<T[, Reason]>`
|
|
1097
|
+
|
|
1098
|
+
Attach callbacks for the resolution and/or rejection of a [`Thenable`][thenable] (`type`).
|
|
1099
|
+
|
|
1100
|
+
```ts
|
|
1101
|
+
type Then<T = unknown, Reason = any> = <Succ = T, Fail = never>(
|
|
1102
|
+
this: any,
|
|
1103
|
+
onfulfilled?: OnFulfilled<T, Succ> | null | undefined,
|
|
1104
|
+
onrejected?: OnRejected<Fail, Reason> | null | undefined
|
|
1105
|
+
) => Thenable<Fail | Succ>
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
#### Type Parameters
|
|
1109
|
+
|
|
1110
|
+
- `T` (`any`, optional)
|
|
1111
|
+
— the previously resolved value
|
|
493
1112
|
- **default**: `unknown`
|
|
1113
|
+
- `Reason` (`any`, optional)
|
|
1114
|
+
— the reason for a rejection
|
|
1115
|
+
- **default**: `any`
|
|
1116
|
+
- `Succ` (`any`, optional)
|
|
1117
|
+
— the next resolved value on success
|
|
1118
|
+
- **default**: `T`
|
|
1119
|
+
- `Fail` (`any`, optional)
|
|
1120
|
+
— the next resolved value on failure
|
|
1121
|
+
- **default**: `never`
|
|
494
1122
|
|
|
495
1123
|
#### Parameters
|
|
496
1124
|
|
|
497
|
-
-
|
|
498
|
-
|
|
499
|
-
|
|
1125
|
+
- `onfulfilled` ([`OnFulfilled<T, Succ>`][onfulfilled] | `null` | `undefined`)
|
|
1126
|
+
— the callback to execute when the thenable is resolved
|
|
1127
|
+
- `onrejected` ([`OnRejected<Fail, Reason>`][onrejected] | `null` | `undefined`)
|
|
1128
|
+
— the callback to execute when the thenable is rejected
|
|
500
1129
|
|
|
501
1130
|
#### Returns
|
|
502
1131
|
|
|
503
|
-
([`
|
|
1132
|
+
([`Thenable<Fail | Succ>`][thenable]) The next [*thenable*][thenable-term]
|
|
1133
|
+
|
|
1134
|
+
### `Thenable<[T]>`
|
|
1135
|
+
|
|
1136
|
+
The completion of an asynchronous operation, and the minimal structural contract required
|
|
1137
|
+
by [`when`][when] to treat a value as asynchronous (`interface`).
|
|
1138
|
+
|
|
1139
|
+
Unlike `PromiseLike`, this interface allows a maybe-callable [`catch`][catch] method, which when present,
|
|
1140
|
+
is used by [`when`][when] to ensure failures are handled without forcing promise allocation.
|
|
1141
|
+
|
|
1142
|
+
Maybe-callable methods are named so because they are not required,
|
|
1143
|
+
and may be a method implementation, `null`, or `undefined`.
|
|
1144
|
+
|
|
1145
|
+
#### Type Parameters
|
|
1146
|
+
|
|
1147
|
+
- `T` (`any`, optional)
|
|
1148
|
+
— the resolved value
|
|
1149
|
+
- **default**: `any`
|
|
1150
|
+
|
|
1151
|
+
#### Properties
|
|
1152
|
+
|
|
1153
|
+
- `catch?` ([`Catch<T>`][catch] | `null` | `undefined`)
|
|
1154
|
+
— attach a callback only to be invoked on rejection
|
|
1155
|
+
- `then` ([`Then<T>`][then])
|
|
1156
|
+
— attach callbacks to be invoked on resolution (fulfillment) and/or rejection
|
|
1157
|
+
- `finally?` ([`Finally<T>`][finally] | `null` | `undefined`)
|
|
1158
|
+
— attach a callback only to be invoked on settlement (fulfillment or rejection)
|
|
1159
|
+
> 👉 **note**: the resolved value cannot be modified from the callback
|
|
504
1160
|
|
|
505
1161
|
## Glossary
|
|
506
1162
|
|
|
507
1163
|
### *awaitable*
|
|
508
1164
|
|
|
509
|
-
A synchronous or [*thenable*][thenable] value.
|
|
1165
|
+
A synchronous or [*thenable*][thenable-term] value.
|
|
510
1166
|
|
|
511
1167
|
### *thenable*
|
|
512
1168
|
|
|
513
|
-
An object or function with a
|
|
1169
|
+
An object or function with a [`then`][then] method.
|
|
514
1170
|
|
|
515
1171
|
JavaScript engines use duck-typing for promises.
|
|
516
|
-
|
|
517
|
-
like [`Promise.resolve`][promise-resolve] and the [`await` keyword][await] like native promises.
|
|
1172
|
+
Arrays, functions, and objects with a `then` method will be treated as promise-like objects, and work with built-in
|
|
1173
|
+
mechanisms like [`Promise.resolve`][promise-resolve] and the [`await` keyword][await] like native promises.
|
|
1174
|
+
|
|
1175
|
+
Some thenables also implement a [`catch`][catch] method (like native promises).
|
|
1176
|
+
When available, [`when`][when] uses it to ensure rejections are handled.
|
|
518
1177
|
|
|
519
1178
|
## Project
|
|
520
1179
|
|
|
@@ -529,30 +1188,83 @@ See [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
|
529
1188
|
This project has a [code of conduct](./CODE_OF_CONDUCT.md).
|
|
530
1189
|
By interacting with this repository, organization, or community you agree to abide by its terms.
|
|
531
1190
|
|
|
1191
|
+
### Sponsor
|
|
1192
|
+
|
|
1193
|
+
This package is intentionally small — and intentionally maintained.
|
|
1194
|
+
|
|
1195
|
+
Small primitives power larger systems.
|
|
1196
|
+
Support long-term stability by sponsoring Flex Development.
|
|
1197
|
+
|
|
532
1198
|
[await]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/await
|
|
533
1199
|
|
|
1200
|
+
[awaitable-term]: #awaitable
|
|
1201
|
+
|
|
534
1202
|
[awaitable]: #awaitablet
|
|
535
1203
|
|
|
536
|
-
[
|
|
1204
|
+
[catch]: #catcht-reason
|
|
1205
|
+
|
|
1206
|
+
[catchable]: #catchablet
|
|
1207
|
+
|
|
1208
|
+
[chain]: #chaint-next-args-this
|
|
1209
|
+
|
|
1210
|
+
[createthenable]: #createthenablet-reason-resultexecutor-options
|
|
1211
|
+
|
|
1212
|
+
[createthenableoptions]: #createthenableoptions
|
|
537
1213
|
|
|
538
1214
|
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
|
|
539
1215
|
|
|
540
1216
|
[esmsh]: https://esm.sh
|
|
541
1217
|
|
|
1218
|
+
[executor]: #executort-reason
|
|
1219
|
+
|
|
1220
|
+
[fail]: #failnext-reason-this
|
|
1221
|
+
|
|
1222
|
+
[finalizable]: #finalizablet
|
|
1223
|
+
|
|
1224
|
+
[finally]: #finallyt
|
|
1225
|
+
|
|
1226
|
+
[finish]: #finishthis
|
|
1227
|
+
|
|
1228
|
+
[iscatchable]: #iscatchabletvalue
|
|
1229
|
+
|
|
1230
|
+
[isfinalizable]: #isfinalizabletvalue
|
|
1231
|
+
|
|
1232
|
+
[ispromise]: #ispromisetvalue-finalizable
|
|
1233
|
+
|
|
1234
|
+
[ispromiselike]: #ispromiseliketvalue
|
|
1235
|
+
|
|
542
1236
|
[isthenable]: #isthenabletvalue
|
|
543
1237
|
|
|
544
|
-
[
|
|
1238
|
+
[onfinally]: #onfinally
|
|
1239
|
+
|
|
1240
|
+
[onfulfilled]: #onfulfilledt-next
|
|
1241
|
+
|
|
1242
|
+
[onrejected]: #onrejectednext-reason
|
|
1243
|
+
|
|
1244
|
+
[options]: #optionst-next-failure-args-error-this
|
|
545
1245
|
|
|
546
1246
|
[promise-resolve]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
|
|
547
1247
|
|
|
548
|
-
[
|
|
1248
|
+
[promise-then]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
|
|
1249
|
+
|
|
1250
|
+
[promiselike]: #promiseliket
|
|
1251
|
+
|
|
1252
|
+
[reject]: #rejectreason
|
|
1253
|
+
|
|
1254
|
+
[resolve]: #resolvet
|
|
549
1255
|
|
|
550
1256
|
[semver]: https://semver.org
|
|
551
1257
|
|
|
552
|
-
[
|
|
1258
|
+
[then]: #thent-reason
|
|
1259
|
+
|
|
1260
|
+
[thenable-term]: #thenable
|
|
1261
|
+
|
|
1262
|
+
[thenable]: #thenablet
|
|
1263
|
+
|
|
1264
|
+
[typescript-eslint]: https://typescript-eslint.io
|
|
553
1265
|
|
|
554
1266
|
[typescript]: https://www.typescriptlang.org
|
|
555
1267
|
|
|
556
|
-
[when]: #whent-next-args-
|
|
1268
|
+
[when]: #whent-next-failure-args-error-this-resultvalue-chain-fail-context-args
|
|
557
1269
|
|
|
558
1270
|
[yarn]: https://yarnpkg.com
|