Skip to content

Commit a7c5181

Browse files
committed
feat: add pChain
1 parent 1753433 commit a7c5181

File tree

6 files changed

+174
-4
lines changed

6 files changed

+174
-4
lines changed

.github/workflows/deploy-docs.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ name: Vercel Deployment Development
33
env:
44
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
55
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
6+
67
on:
78
push:
8-
branches:
9-
- main
9+
tags:
10+
- 'v*'
1011

1112
# This is what will cancel the workflow
1213
concurrency:

docs/.vitepress/locale.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@ export function getLocaleConfig(lang: string) {
158158
},
159159
{
160160
text: t('Promise Utilities'),
161-
items: [{ text: 'delay', link: '/reference/promise/delay' }],
161+
items: [
162+
{ text: 'delay', link: '/reference/promise/delay' },
163+
{ text: 'pChain', link: '/reference/promise/pChain' },
164+
],
162165
},
163166
{
164167
text: t('String Utilities'),

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@nolebase/vitepress-plugin-highlight-targeted-heading": "^2.2.1",
2121
"@shikijs/vitepress-twoslash": "^1.10.3",
2222
"@vitejs/plugin-vue-jsx": "^4.0.0",
23-
"js-utils-es": "^1.0.5",
23+
"js-utils-es": "^1.0.6",
2424
"unocss": "^0.61.3",
2525
"vite": "^5.3.3",
2626
"vitepress": "^1.3.0",

docs/reference/promise/pChain.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# pChain
2+
3+
Utility for managing multiple promises.
4+
5+
## Signature
6+
7+
```typescript
8+
function pChain<T = any>(items?: Iterable<T>): PInstance<T>;
9+
```
10+
11+
### Parameters
12+
13+
- `items` (`Iterable`): An optional iterable of items to chain.
14+
15+
### Returns
16+
17+
(`PInstance<void>`): A `PInstance` object that can be used to chain promises.
18+
19+
## Description
20+
21+
```ts
22+
declare class PInstance<T = any> extends Promise<Awaited<T>[]> {
23+
items: Iterable<T>;
24+
private promises;
25+
get promise(): Promise<Awaited<T>[]>;
26+
constructor(items?: Iterable<T>);
27+
add(...args: (T | Promise<T>)[]): void;
28+
map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>>;
29+
filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>>;
30+
forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void>;
31+
reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U>;
32+
clear(): void;
33+
then(fn?: () => PromiseLike<any>): Promise<any>;
34+
catch(fn?: (err: unknown) => PromiseLike<any>): Promise<any>;
35+
finally(fn?: () => void): Promise<Awaited<T>[]>;
36+
}
37+
```
38+
39+
## Examples
40+
41+
```typescript
42+
import { pChain } from 'js-utils-es/promise';
43+
44+
const items = [1, 2, 3, 4, 5];
45+
46+
await pChain(items)
47+
.map(async i => await multiply(i, 3))
48+
.filter(async i => await isEven(i))
49+
50+
// Result: [6, 12]
51+
```

src/promise/pChain.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { pChain } from './pChain'
3+
4+
const timeout = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
5+
6+
describe('pChain', () => {
7+
it('chain array map', async () => {
8+
expect(
9+
await pChain([1, 2, 3, 4, 5])
10+
.map(async (i) => {
11+
await timeout(10)
12+
return i * i
13+
})
14+
.filter(i => i > 10)
15+
.reduce((a, b) => a + b, 0),
16+
)
17+
.toEqual(41)
18+
})
19+
})

src/promise/pChain.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Internal marker for filtered items
3+
*/
4+
const VOID = Symbol('p-void')
5+
6+
class PInstance<T = any> extends Promise<Awaited<T>[]> {
7+
private promises = new Set<T | Promise<T>>()
8+
9+
get promise(): Promise<Awaited<T>[]> {
10+
const items = [...Array.from(this.items), ...Array.from(this.promises)]
11+
12+
const batch = Promise.all(items)
13+
14+
return batch.then(l => l.filter((i: any) => i !== VOID))
15+
}
16+
17+
constructor(public items: Iterable<T> = []) {
18+
super(() => {})
19+
}
20+
21+
add(...args: (T | Promise<T>)[]) {
22+
args.forEach((i) => {
23+
this.promises.add(i)
24+
})
25+
}
26+
27+
map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>> {
28+
return new PInstance(
29+
Array.from(this.items)
30+
.map(async (i, idx) => {
31+
const v = await i
32+
if ((v as any) === VOID)
33+
return VOID as unknown as U
34+
return fn(v, idx)
35+
}),
36+
)
37+
}
38+
39+
filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>> {
40+
return new PInstance(
41+
Array.from(this.items)
42+
.map(async (i, idx) => {
43+
const v = await i
44+
const r = await fn(v, idx)
45+
if (!r)
46+
return VOID as unknown as T
47+
return v
48+
}),
49+
)
50+
}
51+
52+
forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void> {
53+
return this.map(fn).then()
54+
}
55+
56+
reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U> {
57+
return this.promise.then(array => array.reduce(fn, initialValue))
58+
}
59+
60+
clear() {
61+
this.promises.clear()
62+
}
63+
64+
then(fn?: () => PromiseLike<any>) {
65+
const p = this.promise
66+
if (fn)
67+
return p.then(fn)
68+
else
69+
return p
70+
}
71+
72+
catch(fn?: (err: unknown) => PromiseLike<any>) {
73+
return this.promise.catch(fn)
74+
}
75+
76+
finally(fn?: () => void) {
77+
return this.promise.finally(fn)
78+
}
79+
}
80+
81+
/**
82+
* Utility for managing multiple promises.
83+
*
84+
* @example
85+
* ```js
86+
* const items = [1, 2, 3, 4, 5]
87+
*
88+
* await p(items)
89+
* .map(async i => await multiply(i, 3))
90+
* .filter(async i => await isEven(i))
91+
* // [6, 12]
92+
* ```
93+
*/
94+
export function pChain<T = any>(items?: Iterable<T>): PInstance<T> {
95+
return new PInstance(items)
96+
}

0 commit comments

Comments
 (0)