npm package: @silver-zepp/sequence
Description: Sequence - a JavaScript utility for managing complex chains of synchronous and asynchronous operations with timing control.
Author: Silver, Zepp Health
Latest version: 1.0.3

# Sequence

`Sequence` is a `JavaScript` utility for managing complex chains of synchronous and asynchronous operations with timing control.

## Key Features
- **Linear Readability**: Define sequences of operations in a clear, Arduino-like linear fashion.
- **Timing Control**: Control over delays and execution timing.
- **Async/Sync Mixing**: Combine synchronous and asynchronous tasks.
- **Flow Control**: Pause, resume, and loop functionality for flexible execution.
- **Parallel Execution**: Run tasks in parallel with timeout control.
- **Error Handling**: Built-in error with `.catch()` and cleanup with `.finally()`; `try/catch` out of the box. 
- **State Management**: Track sequence state (idle, running, paused, completed, error).
- **Composability**: Easily compose and reuse sequences.

## Use Cases
- Animation sequencing (move a box from A to B, wait a second, move it to C)
- Game development (cutscenes, scripted events)
- API call orchestration (if this then that)
- Any scenario requiring precise timing and sequencing of operations
- Complex UI interactions

## Examples

```javascript
// install -> npm i @silver-zepp/sequence
import { Sequence } from '@silver-zepp/sequence';

// Example #0: Print Hello World after 1 second
new Sequence()
	.delay(1000)          // wait for 1000ms
	.log("Hello World")   // print a message
	.start()              // autostart this sequence

// Example #1: A little bit more complex Arduino-like execution chain
const seq = new Sequence()
  .log("1. Waiting 500 ms")
  .delay(500)
  .log("2. Waiting 1000 ms")
  .delay(1000)
  .log("3. Waiting 2000 ms")
  .delay(2000)
  .call(() => console.log("===> Custom function called"))
  .log("4. Starting parallel tasks")
  .parallel([
    new Sequence().delay(1000).call(() => console.log("===> task [ 1 ] done")),
    new Sequence().delay(2000).call(() => console.log("===> task [ 2 ] done"))
  ], { wait_for_all: true, timeout: 3000 }) // wait for all parallel tasks to finish? this is done async
  .log("5. All parallel tasks completed or timed out")
  .log("6. All done")
  .loop()
  .start()

// Example #2: async + context passing
function asyncBlockA(resolve) {
  console.log('dummy async op...');
  setTimeout(() => {
    console.log('dummy async op DONE after 2 seconds');
    resolve({ ok: true, data: 'dummy data' });
  }, 2000);
}

function blockB(data) {
  console.log(JSON.stringify(data));
}

const seq = new Sequence()
  .log('Starting blocks A & B sequentially')
  .await(asyncBlockA, { timeout: 3000 }) // timeout of 3s (default 5s)
  .log('Block A executed waiting for 1s before going next')
  .delay(1000)
  .call((result) => blockB(result)) // using `result` from .await() method
  .log('All sequential tasks completed')
  .start();

// Example #3: Animate some things
const seq = new Sequence()
  .log("Starting animation")
  .call(() => circle.add("fade-in")) // call arbitrary function
  .delay(500)
  .call(() => circle.add("move-up"))
  .delay(1000)
  .parallel([ // call multile functions at the same time in async manner
    new Sequence().call(() => circle.add("rotate")),
    new Sequence().call(() => updateText("Animation complete!"))
  ])
  .log("Animation sequence finished")
  .start();

// Example #4: Operate with variables outside the sequence
let check_count = 0;
let is_destroyed = false;
const checker = new Sequence()
  .call(() => {
    check_count++;
    is_destroyed = seq.isDestroyed();
    if (is_destroyed) {
      console.log("Seq state:", JSON.stringify(seq));
      checker.stop();
    } else {
      console.log("Checker CHECKS:", check_count);
    }
  })
  .delay(1000)
  .loop()
  .start()


// Example #5: Basic parallelism
new Sequence()
  .log("Starting parallel tasks")
  .parallel([
    new Sequence().delay(2000).log("Task 1 done (2 sec)"),
    new Sequence().delay(1000).log("Task 2 done (1 sec)"),
    new Sequence().delay(4000).log("..."), // This task will timeout
  ], { mode: Sequence.PARALLEL_MODE_ALL, timeout: 3000 }) // note the mode selector
  .log("All parallel tasks completed")
  .start();

// Example #6: Timescale manipulation
// aka make things slow down or speed up
// 1.0 is the default speed; 
// at 0.5 the sequence will take twice the time
// delay(1000) = 1s but delay(1000) at timescale 0.5 is equal to 2000 (!)
Sequence.SetTimeScale(1.0);  

let count = 0;
let delay_start_time = 0;
const seq = new Sequence({ tick_interval: 50 })
  .call(() => count++)
  .repeat(5) // this will loop the above code, making count = 5
  .log("1. Count:", () => `${count} (index: ${seq.getCurrentTaskIndex()})`)
  .call(() => delay_start_time = Date.now())
  .delay(1000)
  .log("2. After 1s delay")
  .log("Actual delay time:", () => `${Date.now() - delay_start_time}ms`)
  .log("TS:", Sequence.GetTimeScale, "DT:" , Sequence.GetDeltaTime)
  .log("3. Count:", () => `${count} (index: ${seq.getCurrentTaskIndex()})`)
  .repeat(5) // this will loop the whole code from above so the end count will be 5 * 5 = 25
  .log("4. Execution finished")
  .call(()=> setTimeout(after, 300)) // execute external function that will check if the seq is destroyed
  .start();

function after(){
  console.log(seq.isDestroyed())
}

// Example #0 (again): Same old example but with a little bit more explanation
new Sequence()
	.delay(1000)          // wait for 1000ms
	.log("Hello World")   // print a message
	.start()              // autostart this sequence
// Note: the sequence will autodestroy itself after execution
// if you want to rerun it in the future, create the seq with autodestroy flag set to false
const seq = new Sequence({ autodestroy: false })
	.delay(1000)
	.log("Hello World")
// this way you can start the sequence whenever you need and as many times as needed
seq.start();
// ...
seq.start();
```