Skip to content

Commit

Permalink
Refactor tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
vladbasin committed Jul 22, 2024
1 parent 4f9ed3a commit ac64cd4
Show file tree
Hide file tree
Showing 58 changed files with 2,479 additions and 2,103 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [18.x, 20.x, 21.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"editor.formatOnSave": true,
"explorer.autoReveal": false,
"files.exclude": {
"dist/": true,
},
Expand Down
104 changes: 99 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ This library brings elements of functional programming to TypeScript/JavaScript.
## Use cases

### Asynchronous code chaining
Let's assume you have to work with the set of async methods, which load data from backend and return Promise.
Normally you use try\catch, async\await, then\catch, if\then\else to handle results. Readability isn't really good with this approach.
Let's assume you have to work with a set of async methods, which load data from the backend and return a Promise. Normally you use try\catch, async\await, then\catch, if\then\else to handle results. Readability isn't really good with this approach.

```typescript

showLoader();
try {
const wallet = await getWalletAsync();
Expand All @@ -41,7 +39,6 @@ catch (error) {
finally {
hideLoader();
}

```

However, **with this library instead** you can write nice readable chains of methods:
Expand Down Expand Up @@ -98,5 +95,102 @@ Result
.run();
```

### Error processing
In complex systems, it's often necessary to handle errors in a way that avoids multiple processing and overriding, especially when multiple services are involved. For example, errors might be localized, and you need to ensure that an error is processed only once and not overridden by subsequent services.

To achieve this, you can use methods like `withProcessedError` to mark errors as processed. This ensures that subsequent error handling logic does not override the already processed error.

Below code processes failure from `getDataAsync()` call and ensures that error is processed by next service only if it was not processed before:

```typescript
import { Result, ProcessedError } from "@vladbasin/ts-result";

Result
.FromPromise(getDataAsync())
.withProcessedFail(response => accountService.processErrors(response))
.withProcessedFail(response => walletService.processErrors(response)) // will not be called if accountService.processErrors() already processed response and found error
.onFailure((error) => {
// do something with errors
})
.onSuccess((data) => {
// execute logic if getDataAsync call was successful
})
```

### Use as Promises
If you don't want to use `Result` as a return type and continue using `Promise` while benefiting from `Result` functionality, you can always:

- Convert `Result` to `Promise`: `result.asPromise()`
- Convert `Promise` to `Result`: `Result.FromPromise(YOUR_PROMISE)`

### Using Combiner
The `Combiner` class provides several methods to combine multiple `Result` instances into one. This is useful when you need to execute multiple asynchronous operations in parallel and handle their results collectively.

#### Combining Two Results
```typescript
import { Result, Combiner } from "@vladbasin/ts-result";

const result1 = Result.FromPromise(fetchData1());
const result2 = Result.FromPromise(fetchData2());

Combiner.Combine2(result1, result2)
.onSuccess(([data1, data2]) => {
console.log("Data1:", data1);
console.log("Data2:", data2);
})
.onFailure(error => {
console.error("Error:", error);
})
.run();
```

#### Combining Multiple Results

```typescript
import { Result, Combiner } from "@vladbasin/ts-result";

const results = [
Result.FromPromise(fetchData1()),
Result.FromPromise(fetchData2()),
Result.FromPromise(fetchData3())
];

Combiner.CombineMany(results)
.onSuccess(dataArray => {
dataArray.forEach((data, index) => {
console.log(`Data${index + 1}:`, data);
});
})
.onFailure(error => {
console.error("Error:", error);
})
.run();
```

### Controlled Parallel Execution

You can execute multiple actions with given parallelism level (concurrency)

```typescript
import { Result } from "@vladbasin/ts-result";

const factories = [
() => Result.FromPromise(fetchData1()),
() => Result.FromPromise(fetchData2()),
() => Result.FromPromise(fetchData3())
];

Result.CombineFactories(factories, { concurrency: 2 })
.onSuccess(dataArray => {
dataArray.forEach((data, index) => {
console.log(`Data${index + 1}:`, data);
});
})
.onFailure(error => {
console.error("Error:", error);
})
.run();
```

### Other handy API
This library also provides API to **retry(), delay(), and combine()** asynchonous code. See inline comments for more documentation
This library also provides API to **retry(), delay() and others**. See inline comments for more documentation
77 changes: 49 additions & 28 deletions src/Combiner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,33 @@ import { CombineFactoriesOptionsType } from './CombineFactoriesOptionsType';

export class Combiner {
/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines two Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @returns New Result storing an array with values from the two Results.
*/
static Combine2<T1, T2>(r1: Result<T1>, r2: Result<T2>): Result<[T1, T2]> {
return new Result(Promise.all([r1.asPromise(), r2.asPromise()]));
}

/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines three Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @param r3 Third Result.
* @returns New Result storing an array with values from the three Results.
*/
static Combine3<T1, T2, T3>(r1: Result<T1>, r2: Result<T2>, r3: Result<T3>): Result<[T1, T2, T3]> {
return new Result(Promise.all([r1.asPromise(), r2.asPromise(), r3.asPromise()]));
}

/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines four Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @param r3 Third Result.
* @param r4 Fourth Result.
* @returns New Result storing an array with values from the four Results.
*/
static Combine4<T1, T2, T3, T4>(
r1: Result<T1>,
Expand All @@ -36,9 +42,13 @@ export class Combiner {
}

/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines five Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @param r3 Third Result.
* @param r4 Fourth Result.
* @param r5 Fifth Result.
* @returns New Result storing an array with values from the five Results.
*/
static Combine5<T1, T2, T3, T4, T5>(
r1: Result<T1>,
Expand All @@ -53,9 +63,14 @@ export class Combiner {
}

/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines six Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @param r3 Third Result.
* @param r4 Fourth Result.
* @param r5 Fifth Result.
* @param r6 Sixth Result.
* @returns New Result storing an array with values from the six Results.
*/
static Combine6<T1, T2, T3, T4, T5, T6>(
r1: Result<T1>,
Expand All @@ -78,9 +93,15 @@ export class Combiner {
}

/**
* Combines multiple Results into one
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines seven Results into one.
* @param r1 First Result.
* @param r2 Second Result.
* @param r3 Third Result.
* @param r4 Fourth Result.
* @param r5 Fifth Result.
* @param r6 Sixth Result.
* @param r7 Seventh Result.
* @returns New Result storing an array with values from the seven Results.
*/
static Combine7<T1, T2, T3, T4, T5, T6, T7>(
r1: Result<T1>,
Expand All @@ -105,29 +126,29 @@ export class Combiner {
}

/**
* Combines multiple Results into one. Fails if any of the promise fails.
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines an array of Results into one, failing if any of the promises fail.
* @param results Array of Results to combine.
* @returns New Result storing an array with values from the Results.
*/
static CombineMany<T>(results: Result<T>[]): Result<T[]> {
const promises = results.map(result => result.asPromise());

return new Result(Promise.all(promises));
}

/**
* Combines multiple Results into one. Never fails and returns information about which results where successful and which aren't
* @param results Results which can be executed in parallel
* @returns New Result which stores the value of other results
* Combines an array of Results into one, never failing and returning information about which Results succeeded and which failed.
* @param results Array of Results to combine.
* @returns New Result storing an array of PromiseSettledResult objects.
*/
static CombineSettled<T>(results: Result<T>[]) {
return new Result(Promise.allSettled(results.map(result => result.asPromise())));
}

/**
* Combines multiple Result factories into one with concurrency and error handling.
* @param factories Factories which create Results to be executed
* @returns New Result which stores the value of produced results
* Combines multiple Result factories into one, handling concurrency and errors.
* @param factories Array of functions that create Results.
* @param options Optional settings for combining factories.
* @returns New Result storing an array of values produced by the factories.
*/
static CombineFactories<T>(factories: (() => Result<T>)[], options?: CombineFactoriesOptionsType): Result<T[]> {
return Result.FromPromise(
Expand Down
Loading

0 comments on commit ac64cd4

Please sign in to comment.