Options
All
  • Public
  • Public/Protected
  • All
Menu

Ajeeb Coroutines

Unity-inspired Coroutines for the browser and nodejs.

Ajeeb Coroutines are a TypeScript implementation of a similar idea from the Unity 3D engine. They use ES6 generators to turn code that reads sequentially into code that runs across multiple frames. They were designed for the Ajeeb Game Engine but have no dependencies and can be used anywhere.

Installation

npm install nasser/ajeeb-coroutines

Or in a browser you can link to the CDN

<script src="https://cdn.jsdelivr.net/gh/nasser/ajeeb-coroutines@master/build/coroutines.iife.js"></script>

Usage

An instance of an ES6 generator is treated as a coroutine. An instance of the Coroutines class schedules and runs coroutines.

Coroutines.start adds a couroutine to the collection. Coroutines.tick runs every coroutine in the collection up to their next yield statement. Each coroutine remembers the last yield they reached, and the next time Coroutines.tick is called they resume execution from that point.

import { Coroutines } from "ajeeb-coroutines"

let coro = new Coroutines()

coro.start(function* () {
    console.log("hello")    // prints "hello"
    yield;                  // waits until next tick
    console.log("world")  
    console.log("how")      // prints "world" and "how"
    yield; yield;           // waits for two ticks
    console.log("are you?") // prints "are you?"
})

coro.tick()
// prints "hello"
coro.tick()
// prints "world"
// prints "how"
coro.tick()
// does nothing
coro.tick()
// prints "are you?"
// further calls to coro.tick() will do nothing

Coroutines are designed to run across multiple frames. tick can be scheduled to run regularly setInterval or requestAnimationFrame in a browser. This advances every coroutine in the collection automatically once per frame. When scheduled like this, you can think of yield as a way of "waiting one frame".

import { Coroutines } from "ajeeb-coroutines"

let coro = new Coroutines()

coro.start(function* () {
    console.log("hello")    // prints "hello" and waits one frame
    yield;
    console.log("world")    // prints "world" and waits two frames
    yield; yield;
    console.log("how")      // prints "how" and waits three frames
    yield; yield; yield;
    console.log("are")      // prints "are" and waits four frames
    yield; yield; yield; yield;
    console.log("you?")     // prints "you?"
})

setInterval(() => coro.tick(), 1000 / 60) // tick 60 times a second

Generators are normal JavaScript functions. They have access to local variables, closures, arguments, and more. yield can appear anywhere.

import { Coroutines } from "ajeeb-coroutines"

let coro = new Coroutines()

function* hello(repeat) {
    console.log("hello")    // prints "hello" and waits one frame
    yield;
    for(let i=0; i<repeat; i++) { 
        console.log("world")    // prints "world" and wait one frame
        yield;                  // execution will resume from this point 
    }
    console.log("!")     // prints "!"
}

coro.start()

setInterval(() => coro.tick(), 1000 / 60) // tick 60 times a second

This library also exports a number of generically useful coroutines, like wait, that can be combined with your own in powerful ways. A coroutine can be made to wait for another coroutine with the yield* statement. If yield is read as "wait one frame" yield* is read as "wait until this other coroutine completes"

import { Coroutines, wait } from "ajeeb-coroutines"

let coro = new Coroutines()

// schedule coroutines to tick every frame
// in the browser
function runCoroutines() {
    requestAnimationFrame( runCoroutines )
    coro.tick()
}
runCoroutines()

// in node
setInterval(() => coro.tick(), 1000/60)

// you can start coroutines after 
coro.start(function* () {
    console.log("hello")  // prints "hello"
    yield* wait(2)        // wait for 2 seconds
    console.log("world")  // prints "world"
    yield* wait(3)        // waits 3 seconds
    console.log("!!!")    // prints "!!!"
})

// prints "hello" immediately 
// waits 2 seconds
// prints "world" 
// waits 3 seconds
// prints "!!!" 

Coroutines can be treated as normal functions. They take arguments, and can be as complex as needed.

import fs from "fs"
import { Coroutines, waitUntil } from "ajeeb-coroutines"

let coro = new Coroutines()

function* waitForFile(path) {
    console.log("waiting for", path);
    yield* waitUntil(() => fs.existsSync(path))
    console.log("ok");
}

coro.start(waitForFile("nice.txt"))

// in the browser
function runCoroutines() {
    requestAnimationFrame( runCoroutines )
    coro.tick()
}
runCoroutines()

// in node
setInterval(() => coro.tick(), 1000/60)

Index

Classes

Combinator Functions

Coroutine Functions

Other Functions

Combinator Functions

sequence

  • sequence(coros: (Iterator<any> | function)[]): IterableIterator<any>
  • Returns a coroutine that completes each coroutine in coros in turn

    Parameters

    • coros: (Iterator<any> | function)[]

      The coroutines to complete

    Returns IterableIterator<any>

waitFirst

  • waitFirst(coros: Iterator<any>[]): IterableIterator<any>
  • Returns a coroutine that waits for the first coroutine of coros to complete.

    Parameters

    • coros: Iterator<any>[]

      The coroutines to wait for

    Returns IterableIterator<any>

waitLast

  • waitLast(coros: Iterator<any>[]): IterableIterator<any>
  • Returns a coroutine that waits for every coroutine of coros to complete.

    Parameters

    • coros: Iterator<any>[]

      The coroutines to wait for

    Returns IterableIterator<any>

Coroutine Functions

animate

  • animate(obj: any, prop: string, to: any, __namedParameters: object): IterableIterator<any>
  • Animate a parameter.

    todo

    needs way to specify animation speed or time

    see

    setClock

    Parameters

    • obj: any

      The object to mutate

    • prop: string

      The property on obj to mutate

    • to: any

      The final value of obj.prop

    • __namedParameters: object
      • clock: _clock

        The clock function used to measure time. Defaults to the function set by setClock

      • interpolate: function
        • interpolate(a: any, b: any, t: number): number
        • Interpolating function. Given values a and b returns their interpolated value at t, a number between 0 and 1. Defaults to linear interpolation.

          Parameters

          • a: any

            The starting value

          • b: any

            The final value

          • t: number

            The interpolation value, a number between 0 and 1

          Returns number

      • map: function
        • map(x: number): number
        • A function to shape the animation curve. Given a value between 0 and 1 returns a value between 0 and 1. Defaults to the identity function (no shaping).

          Parameters

          • x: number

            A value between 0 and 1

          Returns number

    Returns IterableIterator<any>

wait

  • wait(seconds: number, clock?: _clock): IterableIterator<any>
  • Wait for a number of seconds.

    see

    setClock

    Parameters

    • seconds: number

      How many seconds to wait

    • Default value clock: _clock = _clock

      A function that returns the elapsed application time in seconds, defaults to the function assigned by setClock

    Returns IterableIterator<any>

waitFrames

  • waitFrames(n: number): IterableIterator<any>
  • Wait for a number of frames.

    Parameters

    • n: number

      How many frames to wait

    Returns IterableIterator<any>

waitUntil

  • waitUntil(f: function): IterableIterator<any>
  • Wait until a function f returns true.

    Parameters

    • f: function

      A function to execute every frame. When f returns truthy this coroutine completes.

        • (): boolean
        • Returns boolean

    Returns IterableIterator<any>

waitWhile

  • waitWhile(f: function): IterableIterator<any>
  • Wait while a function f returns true.

    Parameters

    • f: function

      A function to execute every frame. When f returns falsey this coroutine completes.

        • (): boolean
        • Returns boolean

    Returns IterableIterator<any>

Other Functions

setClock

  • setClock(f: function): void
  • Sets a new clock function.

    The clock function returns the elapsed application time in seconds. It is called by some coroutines to measure the passage of time. defaults to performance.now() / 1000

    Parameters

    • f: function

      New clock function

        • (): number
        • Returns number

    Returns void

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Type alias with type parameter
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc