Skip to content

Commit 3f7bea3

Browse files
authored
docs: improved documentation (#3)
* docs: improved documentation * npm test -- --coverage * fix title * run ci on any push
1 parent 1ac4a74 commit 3f7bea3

File tree

9 files changed

+95
-52
lines changed

9 files changed

+95
-52
lines changed

.github/labeler.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
feature: [ 'feature-*', 'feat-*' ]
22
bugfix: [ 'fix-*', 'bugfix-*' ]
33
chore: [ 'chore-*' ]
4+
documentation: [ 'doc-*', 'docs-*' ]

.github/release-drafter.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
name-template: 'v$NEXT_PATCH_VERSION 🌈'
2-
tag-template: 'v$NEXT_PATCH_VERSION'
1+
name-template: 'v$RESOLVED_VERSION 🌈'
2+
tag-template: 'v$RESOLVED_VERSION'
33
categories:
44
- title: '🚀 Features'
55
labels:
@@ -11,10 +11,23 @@ categories:
1111
- 'bugfix'
1212
- 'bug'
1313
- title: '🧰 Maintenance'
14-
label: 'chore'
14+
labels:
15+
- 'chore'
16+
- 'documentation'
1517
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
1618
exclude-labels:
1719
- 'skip-changelog'
20+
version-resolver:
21+
major:
22+
labels:
23+
- 'major'
24+
minor:
25+
labels:
26+
- 'minor'
27+
patch:
28+
labels:
29+
- 'patch'
30+
default: patch
1831
template: |
1932
## Changes
2033

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: "Pull Request Labeler"
2+
3+
on:
4+
push:
5+
branches: "**"
6+
7+
jobs:
8+
ci:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v3
12+
- uses: actions/setup-node@v3
13+
with:
14+
node-version: 18
15+
- run: npm ci
16+
- run: npm test -- --coverage

.github/workflows/labeler.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
# file with configuration. For more information, see:
66
# https://github.com/actions/labeler
77

8-
name: Labeler
8+
name: "Pull Request Labeler"
99
on: [pull_request_target]
1010

1111
jobs:
12-
label:
13-
12+
labeler:
1413
runs-on: ubuntu-latest
1514
permissions:
1615
contents: read
1716
pull-requests: write
18-
1917
steps:
2018
- uses: actions/labeler@v4
2119
with:
File renamed without changes.

README.md

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
# InBatches (📦📦📦)
1+
# @InBatches(📦,📦,📦,...)
22

3-
InBatches is a zero-dependency TypeScript library that provides a convenient way to batch and execute asynchronous
4-
operations in a controlled manner. This library is especially useful for scenarios where you need to perform multiple
5-
asynchronous operations efficiently, such as when making network requests or performing database queries.
3+
InBatches is a zero-dependency generic TypeScript library that provides a convenient way to batch executions that runs
4+
asynchronous.
65

7-
Heavily inspired by [graphql/dataloader](https://github.com/graphql/dataloader) but better 😜
6+
It is designed to be used as part of your application's data fetching layer to provide a consistent API over various
7+
backends and reduce requests to those backends via batching.
8+
9+
This library is especially useful for scenarios where you need to perform multiple asynchronous operations efficiently,
10+
such as when making network requests or performing database queries.
11+
12+
Heavily inspired by [graphql/dataloader](https://github.com/graphql/dataloader) but using classes and decorators 😜
813

914
## Table of Contents
1015

@@ -27,12 +32,13 @@ npm install inbatches
2732

2833
## Usage
2934

30-
### Basic Usage
35+
### Using the `Batcher` Class
3136

3237
```typescript
3338
import { Batcher } from 'inbatches';
3439

3540
// Define a class that extends Batcher and implements the `run` method
41+
// the `run` method will be called with an array of keys collected from the `enqueue` method
3642
class MyBatcher extends Batcher<number, string> {
3743
async run(ids: number[]): Promise<string[]> {
3844
// Perform asynchronous operations using the keys
@@ -44,16 +50,14 @@ class MyBatcher extends Batcher<number, string> {
4450
// Create an instance of your batcher
4551
const batcher = new MyBatcher();
4652

47-
// Enqueue keys for batching and execution
48-
const resultPromise1 = batcher.enqueue(1);
49-
const resultPromise2 = batcher.enqueue(2);
50-
51-
resultPromise1.then(result => {
52-
console.log(result); // Output: "{ id: 1, name: 'Result for key 1' }"
53+
// Enqueue keys for batched execution
54+
const result = [1, 2, 3, 4, 5].map(async id => {
55+
return await batcher.enqueue(id);
5356
});
5457

55-
resultPromise2.then(result => {
56-
console.log(result); // Output: "{ id: 2, name: 'Result for key 2' }"
58+
// The result will be an array of results in the same order as the keys
59+
result.then(results => {
60+
console.log(results); // Output: [{ id: 1, name: 'Result for key 1' }, ...]
5761
});
5862
```
5963

@@ -65,10 +69,13 @@ The library also provides a decorator called `InBatches` that you can use to bat
6569
import { InBatches } from 'inbatches';
6670

6771
class MyService {
68-
@InBatches()
72+
73+
// (optional) overloaded method, where you define the keys as `number` and the return type as `string` for typings
74+
async fetch(keys: number): Promise<string>;
75+
76+
// in reality the Decorator will wrap this method and it will never be called with a single key :)
77+
@InBatches() // This method is now batch-enabled
6978
async fetch(keys: number | number[]): Promise<string | string[]> {
70-
// This method is now batch-enabled
71-
// Perform asynchronous operations using the keys
7279
if (Array.isArray(keys)) {
7380
return this.db.getMany(keys);
7481
}
@@ -80,16 +87,13 @@ class MyService {
8087

8188
const service = new MyService();
8289

83-
// Enqueue keys for batching and execution
84-
const resultPromise1 = service.fetch(1);
85-
const resultPromise2 = service.fetch(2);
86-
87-
resultPromise1.then(results => {
88-
console.log(results); // Output: { id: 1, name: 'Result for key 1' }
90+
const result = [1, 2, 3, 4, 5].map(async id => {
91+
return await service.fetch(id);
8992
});
9093

91-
resultPromise2.then(results => {
92-
console.log(results); // Output: { id: 2, name: 'Result for key 2' }
94+
// The result will be an array of results in the same order as the keys
95+
result.then(results => {
96+
console.log(results); // Output: [{ id: 1, name: 'Result for key 1' }, ...]
9397
});
9498
```
9599

@@ -100,7 +104,9 @@ resultPromise2.then(results => {
100104
An interface to specify options for the batcher.
101105

102106
- `maxBatchSize`: The maximum number of keys to batch together. Default is `25`.
103-
- `delayWindowInMs`: The delay window in milliseconds before dispatching the batch. Default is `undefined`.
107+
- `delayWindowInMs`: (not recommended) The delay window in milliseconds before dispatching the batch. Default
108+
is `undefined` and will use `process.nextTick` to dispatch the batch, which is highly efficient and fast. Only use
109+
this if you really want to accumulate promises calls in a window of time before dispatching the batch.
104110

105111
### `Batcher<K, V>` Class
106112

@@ -116,15 +122,19 @@ A decorator function that can be applied to methods to enable batching.
116122
- Usage: `@InBatches(options?: BatcherOptions)`
117123
- Example:
118124

119-
```typescript
120-
class MyService {
121-
@InBatches({ maxBatchSize: 10 })
122-
async fetchResults(keys: number | number[]): Promise<string | string[]> {
123-
// Batch-enabled method logic
124-
}
125-
}
126-
```
125+
```typescript
126+
class MyService {
127127

128+
// (optional) overloaded method, where you define the keys as `number` and the return type as `string` for typings
129+
async fetchResults(keys: number): Promise<string>
130+
131+
@InBatches({ maxBatchSize: 10 })
132+
async fetchResults(keys: number | number[]): Promise<string | string[]> {
133+
// Batch-enabled method logic
134+
}
135+
}
136+
```
137+
128138
## Contributing
129139

130140
Contributions are welcome! Feel free to open issues or submit pull requests on

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "inbatches",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"private": false,
55
"license": "MIT",
66
"repository": {

src/batcher.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export interface BatcherOptions {
33
delayWindowInMs?: number;
44
}
55

6-
interface Execution<K> {
6+
interface Callback<K> {
77
key: K;
88
resolve: Function;
99
reject: Function;
@@ -12,15 +12,15 @@ interface Execution<K> {
1212
class Batch<K, V> {
1313
public active = true;
1414
public readonly cache = new Map<K, Promise<V>>();
15-
public readonly executions: Execution<K>[] = [];
15+
public readonly callbacks: Callback<K>[] = [];
1616

1717
append(key: K) {
1818
if (this.cache.has(key)) {
1919
return this.cache.get(key);
2020
}
2121

2222
const promise = new Promise<V>((resolve, reject) => {
23-
this.executions.push({ key, resolve, reject });
23+
this.callbacks.push({ key, resolve, reject });
2424
});
2525

2626
this.cache.set(key, promise);
@@ -54,7 +54,7 @@ export abstract class Batcher<K, V> {
5454
private getCurrentBatch() {
5555
// if there is a current batch, and it has not been dispatched, and it is not full
5656
if (this.current) {
57-
if (this.current.active && this.current.executions.length < this.options.maxBatchSize) {
57+
if (this.current.active && this.current.callbacks.length < this.options.maxBatchSize) {
5858
return this.current;
5959
}
6060
}
@@ -82,12 +82,12 @@ export abstract class Batcher<K, V> {
8282
private dispatch(batch: Batch<K, V>) {
8383
batch.active = false;
8484

85-
if (batch.executions.length === 0) {
85+
if (batch.callbacks.length === 0) {
8686
return;
8787
}
8888

8989
try {
90-
this.run(batch.executions.map(callback => callback.key))
90+
this.run(batch.callbacks.map(callback => callback.key))
9191
.then(values => this.fulfill(batch, values))
9292
.catch(error => this.reject(batch, error));
9393
} catch (error) {
@@ -96,15 +96,15 @@ export abstract class Batcher<K, V> {
9696
}
9797

9898
private fulfill(batch: Batch<K, V>, values: (V | Error)[]) {
99-
batch.executions.forEach((callback, index) => {
99+
batch.callbacks.forEach((callback, index) => {
100100
const value = values[index];
101101
if (value instanceof Error) callback.reject(value);
102102
else callback.resolve(value);
103103
});
104104
}
105105

106106
private reject(batch: Batch<K, V>, error: Error) {
107-
batch.executions.forEach(callback => {
107+
batch.callbacks.forEach(callback => {
108108
callback.reject(error);
109109
});
110110
}

src/decorator.spec.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@ class RunInBatches {
44
constructor(private id: string = '') {
55
}
66

7+
8+
async getAll(keys: string): Promise<string>;
9+
710
@InBatches()
8-
async getAll(keys: string | string[]) {
11+
async getAll(keys: string | string[]): Promise<string | string[]> {
912
if (Array.isArray(keys)) {
1013
return keys.map((key, index) => `${key}-index-${index}-${this.id}`);
1114
}
1215
throw new Error('Should not be called with single key');
1316
}
1417

18+
async getCouple(keys: string): Promise<string>;
19+
1520
@InBatches({ maxBatchSize: 2 })
16-
async getCouple(keys: string | string[]) {
21+
async getCouple(keys: string | string[]): Promise<string | string[]> {
1722
if (Array.isArray(keys)) {
1823
return keys.map((key, index) => `${key}-index-${index}-${this.id}`);
1924
}

0 commit comments

Comments
 (0)