# AsyncStatus JS

Manages the status of an async process.

- Built-in TypeScript declarations.
- Built-in Javascript / TypeScript map declarations.
- Comprehensive unit testing.
- [Available at npmjs.com](https://www.npmjs.com/package/@bugtamer/async-status)
- [Source Code](https://github.com/bugtamer/async-status-js)

## Table Of Content

- [Installation](#installation)
  - [As project dependency](#as-project-dependency)
  - [As script dependency](#as-script-dependency)
- [Basic usage snippets](#basic-usage-snippets)
  - [async / await](#async-await)
  - [Observable](#observable)
- [Status management / Class interface](#status-management-class-interface)
  - [Use](#use)
  - [Check attempt stats](#check-attempt-stats)
  - [Check current state](#check-current-state)
  - [Check last outcome state](#check-last-outcome-state)
  - [Measure the time](#measure-the-time)
- [Final notes](#final-notes)
- [Examples](#examples)

## Installation

### As project dependency

`npm i @bugtamer/async-status`

[Available at npmjs.com](https://www.npmjs.com/package/@bugtamer/async-status)

### As script dependency

['require' demo example](https://runkit.com/bugtamer/async-status)

```javascript
const bugtamer = require("@bugtamer/async-status")
const dataAsyncStatus = new bugtamer.AsyncStatus();
```

or

```javascript
const bugtamer = require("@bugtamer/async-status/lib/async-status")
const dataAsyncStatus = new bugtamer.AsyncStatus();
```

['import' demo example](https://github.com/bugtamer/ng-async-status-example)

```javascript
import { AsyncStatus } from '@bugtamer/async-status/lib/async-status';
const dataAsyncStatus = new AsyncStatus();
```

## Basic usage snippets

### async / await

```javascript
dataAsyncStatus.start();
try {
    data = await fetchData();
    dataAsyncStatus.end();
} catch (error) {
    dataAsyncStatus.abort();
}
```

### Observable

```javascript
dataAsyncStatus.start();
const subscription = fetchData().subscribe(
    response => {
        data = response;
        dataAsyncStatus.end();
    },
    error => {
        dataAsyncStatus.abort();
    }
);
```

## Status management / Class interface

### Use

| Current State | Method called / Sentence | Outcome        |
| ------------- | ------------------------ | -------------- |
|               | `new AsyncStatus()`      | idle state     |
| idle          | `start()`                | ongoing state  |
| ongoing       | `end()`                  | idle state     |
| ongoing       | `abort()`                | idle state     |
| ongoing       | `start()`                | Throw an error |
| idle          | `end()`                  | Throw an error |
| idle          | `abort()`                | Throw an error |

Do not try to manage these errors, just fix your code.
They point out that some method should never have called.

### Check attempt stats

| Sentence                              | Description                              |
|---------------------------------------|------------------------------------------|
| `dataAsyncStatus.attempts`            | returns the number of calls to `start()` |
| `dataAsyncStatus.successfulAttempts`  | returns the number of calls to `end()`   |
| `dataAsyncStatus.failedAttempts`      | returns the number of calls to `abort()` |
| `dataAsyncStatus.resetAttemptStats()` | all previous counters are set to 0       |

### Check current state

In this section we understand by _call_ a call to any of the following methods: `start()`, `end()` or `abort()`.

#### Idle State

There is no process activity.

| `dataAsyncStatus.isIdle`                                                    | Returns |
|-----------------------------------------------------------------------------|---------|
| When `start()` was never executed or the last call was `end()` or `abort()` | `true`  |
| In any other case                                                           | `false` |

#### Ongoing state

There is a process in progress.

| `dataAsyncStatus.isOngoing`                                                                       | Returns |
|---------------------------------------------------------------------------------------------------|---------|
| When the last call was `start()` and therefore neither `end()` nor `abort()` have been called yet | `true`  |
| In any other case                                                                                 | `false` |

### Check last outcome state

In this section we understand by _call_ a call to any of the following methods: `start()`, `end()` or `abort()`.

#### A successful outcome

| `dataAsyncStatus.wasSuccessful`         | Returns |
|-----------------------------------------|---------|
| When `end()` was the last method called | `true`  |
| In any other case                       | `false` |

#### A failed outcome

| `dataAsyncStatus.wasFailed`               | Returns |
|-------------------------------------------|---------|
| When `abort()` was the last method called | `true`  |
| In any other case                         | `false` |

### Measure the time

In milliseconds (ms):

| `dataAsyncStatus.elapsedTime`                                                 | Returns                                                          |
|-------------------------------------------------------------------------------|------------------------------------------------------------------|
| when `start()` was never called                                               | `AsyncStatus.UNDEFINED_TIME` (`-1`)                              |
| when `start()` was called but `end()` or `abort()` has not yet been called    | Time elapsed since the call to `start()` to current time         |
| when `start()` was called and eventually `end()` or `abort()` was also called | Elapsed time from call to `start()` to `end()` or `abort()` call |

## Final notes
- Using a single instance of `AsyncStatus` to control multiple independent asynchronous processes that overlap in time could lead to erratic behavior in your program.

- `start()` throws an error when is called more than `Number.MAX_SAFE_INTEGER` times (although is nearly unreachable).

## Examples

- [Angular demo example](https://bugtamer.github.io/ng-async-status-example/)
- [Check out](https://runkit.com/bugtamer/async-status) the following example at RunKit:

```javascript
// const bugtamer = require("@bugtamer/async-status/lib/async-status")
const bugtamer = require("@bugtamer/async-status")


function showStats(asyncStatus, message) {
    console.log(message)
    console.log(`  - Attempts:     ${asyncStatus.attempts}`)
    console.log(`    - successful: ${asyncStatus.successfulAttempts}`)
    console.log(`    - failed:     ${asyncStatus.failedAttempts}`)
    console.log(`  - State:`)
    console.log(`    - idle:       ${asyncStatus.isIdle}`)
    console.log(`    - ongoing:    ${asyncStatus.isOngoing}`)
    console.log(`  - Outcome:`)
    console.log(`    - successful: ${asyncStatus.wasSuccessful}`)
    console.log(`    - failed:     ${asyncStatus.wasFailed}`)
    console.log(`  - Time elapsed: ${asyncStatus.elapsedTime} ms`)
}


// Let's show where the Internation Space Station currently is.
console.log("Let's see where the ISS is with Node " + process.version);

// We can use any package from NPM since they are all built in.
var getJSON = require("async-get-json"); 


const status = new bugtamer.AsyncStatus();
showStats(status, 'new AsyncStatus()')

status.start()
showStats(status, 'start()')


const url = "http://api.open-notify.org/iss-now.json"; // change it to make it fail
try {
    // And we can use ES7 async/await to pull the ISS's position from the open API.
    var result = await getJSON(url);

    status.end()
    showStats(status, 'end()')
} catch (error) {
    status.abort()
    showStats(status, 'abort()')
}


if (!!result) {
    // RunKit will automatically display the last statement and try to find its best representation:
    result.iss_position;
}
```

### Example output

```log
Let's see where the ISS is with Node v14.20.1
new AsyncStatus()
  - Attempts:     0
    - successful: 0
    - failed:     0
  - State:
    - idle:       true
    - ongoing:    false
  - Outcome:
    - successful: false
    - failed:     false
  - Time elapsed: -1 ms
start()
  - Attempts:     1
    - successful: 0
    - failed:     0
  - State:
    - idle:       false
    - ongoing:    true
  - Outcome:
    - successful: false
    - failed:     false
  - Time elapsed: 1 ms
end()
  - Attempts:     1
    - successful: 1
    - failed:     0
  - State:
    - idle:       true
    - ongoing:    false
  - Outcome:
    - successful: true
    - failed:     false
  - Time elapsed: 75 ms
```