Skip to content

Commit

Permalink
changing readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlad Ioffe committed Jan 9, 2021
1 parent 27e4434 commit 75d01da
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 326 deletions.
329 changes: 3 additions & 326 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

This library was highly inspired by lodash but uses decorators to implement its util methods.
The lib can be used both in node and in web application, it is built to be **tree-shakable so you can use it even if you need a specific decorator**.
Also, note that this library can be used both as **functions and decorators**,

<a href="https://opencollective.com/utils-decorators#backers" target="_blank"><img src="https://opencollective.com/utils-decorators/backers.svg?width=890"></a>

Expand All @@ -16,332 +17,8 @@ npm i utils-decorators

Please note that the decorators are **working perfectly with plain JavaScript** code as well as with TypeScript.


Usage example:
```typescript
import {before} from 'utils-decorators';

class Test {
@before<Test>({func: 'goo'})
foo() {
console.log('will run after');
}

goo() {
console.log('will run before foo');
}
}

const t = new Test();
```



## @after (method)
Invocation of the decorated method will cause execution of the provided `func` method after the invocation of the decorated method.

```typescript
function after(config: AfterConfig): Afterable;

interface AfterConfig {
func: Function | string;
wait?: boolean;
}
```

- `func`: the function (`Function`) or the method name (`string`), see notes for more details, to be invoked after the decorated method.
- `wait`: should the invocation of the `func` method be delayed to the point when the decorated method will be resolved.


## @before (method)
Invocation of the decorated method will cause execution of the provided `func` method before the invocation of the decorated method.

```typescript
function before(config: BeforeConfig): Beforable;

interface BeforeConfig {
func: Function | string;
wait?: boolean;
}
```

- `func`: the function (`Function`) or the method name (`string`), see notes for more details, to be invoked before the decorated method.
- `wait`: should the invocation of the decorated method be delayed to the point when `func` will be resolved.


## @cancelPrevious (method)
Invocation of the decorated method will cause the a rejection of the previous invocation with an error of `CancelPromise` type.

```typescript
function cancelPrevious(): CancelPreviousable<T, D>;
```


## @debounce (method)
Causes a delay in the invocation of the decorated method by given time (in ms), if during the delay another invocation will happen, the delay will be restarted.

```typescript
function debounce(delayMs: number): Debouncable;
```


## @delay (method)
Causes a delay in the invocation of the decorated method by given time (in ms).

```typescript
function delay(delayMs: number): Delayable;
```
## @memoize (method)
Memoizes the response that is being returned by the decorated method.
Be default the key of the cached value will be the serialized (`JSON.stringify`) value of the provided arguments.
You can supply your own key resolver.
Also, you can provide your own cache, it has to implement the `GetterSetter<D>` interface, by default the decorator is using a simple `Map<string, Promise<D>>`.

```typescript
function memoize<D>(config: MemoizeConfig<D>): Memoizable<D>;
function memoize<D>(expirationTimeMs: number): Memoizable<D>;

interface MemoizeConfig<T, D> {
cache?: Cache<D>;
keyResolver?: KeyResolver | keyof T;
expirationTimeMs?: number;
}
```

- `cache`: A cache object the previous values would be stored, needs to implement the `Cahce<D> interface`.
- `keyResolver`: A custom resolver for the cache key.
- `expirationTimeMs`: A TTL (time to leave) the cache. *If not provided the cache will be never cleared*


## @memoizeAsync (method)
Memoizes the promise that being returned by the decorated method.
If the promise would be rejected, the promise won't be memoized.
Another great feature of this decorator is that it delegates requests, for example if the same method has been called more than one time after the promise was resolved,
only one invocation of the decorated method will be invoked.

Be default the key of the cached value will be the serialized (`JSON.stringify`) value of the provided arguments.
You can supply your own key resolver.
Also, you can provide your own cache, it has to implement the `GetterSetter<D>` interface, by default the decorator is using a simple `Map<string, Promise<D>>`.

```typescript
function memoizeAsync<D>(config: MemoizeAsyncConfig<D>): AsyncMemoizable<D>;
function memoizeAsync<D>(expirationTimeMs: number): AsyncMemoizable<D>;
function memoizeAsync<D>(expirationTimeMs: number): AsyncMemoizable<D>;

type MemoizeAsyncConfig<T, D> = MemoizeConfig<T, D>
```
See `MemoizeConfig<T, D>` above.
## @onError (method)
This decorator will catch errors thrown from the decorated method and invoke the provided `func` function.
If the decorated method returns a `Promise` the `wait` property should be set to true in order to handle the promise rejection correctly.
```typescript
function onError(config: OnErrorConfig): OnErrorable;

interface OnErrorConfig {
func: (e: error, args: any[]) => any | string;
}
```

- `func`: the function (`Function`) or the method name (`string`), see notes for more details, to be invoked on an error of the decorated method.
- `wait`: should the invocation of the decorated method be delayed to the point when `func` will be resolved/rejected.


## @readonly (property)
This decorator prevents setting new values to decorated property.

```typescript
function readonly<T>(target: T, key: keyof T): void;
```


## @refreshable (property)
This decorator provides an ability to access a property which value is being updated over and over in a given interval (in ms) by the returned value of the provided method. Note that the method can also return a promise which will be resolved after each interval.
In order to cancel the refreshment of the data you need to set the decorated value to null (this is very important note to prevent memory leaks). Setting any other value will be ignored.
Another important note is that the initial value of the decorated attribute must not be `null`. Any other value should be fine.

```typescript
function refreshable<D>(dataProvider: Method<D> | Method<Promise<D>>, intervalMs: number): Refreshable;

interface RefreshableConfig<D> {
dataProvider: Method<D> | Method<Promise<D>>;
intervalMs: number;
}
```

- `dataProvider`: the function that will provide the data to the decorated attribute.
- `intervalMs`: the time interval (in milliseconds) in which the data will be refreshed.


## @throttle (method)
Invocation of the decorated method will happen immediately, but if another invocation of this method will happen during the provided time (in ms) it will be ignored.

```typescript
function throttle(delayMs: number): Throttable;
```


## @execTime (method)
Measures the time that takes for the decorated method to response.
By default will log the result using `console.info()` but this can be changed by providing your own reporter function.

```typescript
function execTime<T>(arg?: ReportFunction | string): ExactTimeReportable<T>
```

- `ReportFunction`: the function that will be invoked after the execution of decorated method. Also can be provided as `string` this way it will invoke the method that is name as the provided string. If no value will be provided then the execution time will be logged with `console.info`.
```typescript
type ReportFunction = (data?: ExactTimeReportData) => any;

type ExactTimeReportData = {
args: any[];
result: any;
execTime: number;
}
```
## @retry (method)
Retries execution of the decorated method. The method will be invoked extra x + 1 (where x is the retries values) in the worst case (when all invocation failed).
You can provide you own delay time (or delay times array) between the invocations - see details below. Note that the default delay between retries is 1000ms.
```typescript
function retry<T>(config: RetryInput): Retryable<T>
```

```typescript
type RetryInput = number | number[] | {
retries: number;
delay: number;
};
```

## @timeout (method)
Will throw an error (`TimeoutError`) if the decorated method returned promise won't be resolved withing the provided timeout (timeout is in milliseconds).

```typescript
function timeout<T>(ms: number): Timeoutable<T>
```

- `ms`: time in milliseconds for waiting for the promise to be resolved.

## @multiDispatch (method)
Will invoke the decorated method the amount of provided time in parallel. This decorator is useful when you want to increase the chance of the decorated method to return a value.
Note that the fastest resolved promise will resolve the decorated method and in case where all dispatched will fail the last error would be of the last rejected promise.

```typescript
function multiDispatch<T>(dispatchesAmount: number): MultiDispatchable<T>
```

- `dispatchesAmount`: the amount of invocations to generate simultaneously.

----

## @delegate (method)
For a given input, if within the time period of the resolving of the promise of the first invocation the decorated method was invoked multiple times (with the same input) the response would be the promise that was generated by the first invocation.
This way this decorator reduces the amount of calls to the implementation of the decorated method, for example accessing the database.

```typescript
function delegate<T>(keyResolver?: (...args: any[]) => string): MultiDispatchable<T>
```

- `keyResolver`: A custom resolver for caching the returned promise.

----

## @rateLimit (method)
Limits the amount of calls to the decorated method in a given time period. This decorator can support both local (in-memory) counter or a distributed one.

```typescript
function reateLimit<T, D>(config: RateLimitConfigs) => string): RateLimitable<T, D>
```

```typescript
interface RateLimitConfigs<T = any> {
timeSpanMs: number;
allowedCalls: number;
keyResolver?: ((...args: any[]) => string) | keyof T;
rateLimitCounter?: RateLimitCounter;
rateLimitAsyncCounter?: RateLimitAsyncCounter;
exceedHandler?: () => void;
}
interface RateLimitCounter {
inc: (key: string) => void;
dec: (key: string) => void;
getCount: (key: string) => number;
}
interface RateLimitAsyncCounter {
inc: (key: string) => Promise<void>;
dec: (key: string) => Promise<void>;
getCount: (key: string) => Promise<number>;
}
```

- `timeSpanMs`: the time window for which the rate-limit should hold the counts (time in milliseconds).
- `allowedCalls`: the amount of call allowed in the provided `timeSpanMs`.
- `keyResolver`: Sometimes you want to group your calls and only then apply the rate-limit. By providing the `keyResolver` you can group your calls by the provided arguments.
- `rateLimitCounter`: You can provide your custom rate limit counter.
- `rateLimitAsyncCounter`: You can provide your custom async rate limit counter - it should implement the `RateLimitCounter` interface. Note that in such cases it is expected that the decorated method is returning a promise.
- `exceedHandler`: There are scenarios in which you want to handle the cases in which the amount of calls exceeds the allowed amount, by providing your custom function you will have this control. By default the decorator will throw an `Error`.
----

## @throttleAsync (method)
For a given limit, the decorator will invoke the decorated method jus the allowed times. Once one of the promises will be resolved the next call in queue will be invoked,

```typescript
function throttleAsync<T, D>(limit? = 1) => Promise<D>): Decorator<T>
```

- `limit`: The limit of allowed concurrent calls.

----

## Notes:
**Class methods:** some decorators expect you to provide a function as one of their attributes or arguments, for example in the `@before`.
Because of the way decorators currently work in JavaScript, there is no way to provide a class method from the same context. We will continue withe the `@before` example, the following code won't work:

```typescript
class Worker {
fetchTasks(): Promise<void> {
...
}
@before({
func: this.fetchTasks.bind(this),
wait: true
})
doWork(workId: number): Promise<void> {
...
}
}
```

When the `@before` decorator code will be executed the instance of the class still won't exist, and this will cause `this` to be `undefined`.
To overcome this issue, instead of providing a reference to the method you can provide the method name:

```typescript
class Worker {
fetchTasks(): Promise<void> {
...
}

@before({
func: 'fetchTasks',
wait: true
})
doWork(workId: number): Promise<void> {
...
}
}
```

This way, during runtime, we can trigger the provided method by it's name with the `this` (class) context.
# Docs
check the <a href="https://vlio20.github.io/utils-decorators/" target="_blank">documentation</a>

# Feedback
I will be more than happy to hear your feedback, don't hesitate to ask any question or open an issue. PRs are always welcomed :)
Expand Down
3 changes: 3 additions & 0 deletions _index.html
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@
<div class="ud-section-title">Installation</div>
<div class="ud-section-content">
<p class="ud-description">
This library was highly inspired by lodash but uses decorators to implement its util methods.
The lib can be used both in node and in web application, it is built to be <b>tree-shakable so you can use it even if you need a specific decorator</b>.

To install the lib via npm please follow the command bellow:
</p>
<pre class="ud-code">
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@
<div class="ud-section-title">Installation</div>
<div class="ud-section-content">
<p class="ud-description">
This library was highly inspired by lodash but uses decorators to implement its util methods.
The lib can be used both in node and in web application, it is built to be <b>tree-shakable so you can use it even if you need a specific decorator</b>.

To install the lib via npm please follow the command bellow:
</p>
<pre class="ud-code">
Expand Down

0 comments on commit 75d01da

Please sign in to comment.