Sequence
is a JavaScript
utility for managing complex chains of synchronous and asynchronous operations with timing control.
- 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.
- 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
// 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();