Skip to content

dizmo/functions-queued

Repository files navigation

NPM version Build Status Coverage Status

@dizmo/functions-queued

Provides two functions queued and auto, where the latter can also be accessed via queued.auto. The queued function takes as argument another function e.g. fn, which is then queued for execution. During its invocation fn receives an extra argument – a next function – which needs to be invoked within the body of fn to continue processing the queue.

The queued function creates a separate queue defined by the name of the provided function: This means that if multiple functions e.g. f1 and f2 need to be queued together, then they need to be wrapped with functions using the same name (e.g. fn). If only anonymous functions are provided then the internal queue is named with a random identifier.

Functions with the same name will be put into the same queue, and class methods with the same class plus method name will also be put into the same queue!

The auto function takes a boolean flag and returns then a queue e.g. qn (for the provided function). If the flag is set to true then the queue start dequeueing immediately, and otherwise qn.next is required to be invoked to trigger dequeueing; by default queued starts dequeueing immediately.

Both the queued and auto functions accepts options, which enable dequeueing (a)synchronously and also support a mechanism to acquire and release (global) locks. By default dequeueing is performed synchronously without the usage of a lock. Also, an option to limit the maximum queue size can provided.

Further, by using a @queued.decorator, class methods can be decorated to turn them into queues, where the same naming rules as explained above apply. However, each method name is prepended with the corresponding class name; i.e. two methods with the same name but from to differently named classes will be put into to different queues.

Instead of using the provided next function to continue dequeueing, the wrapped function can also return a promise for a truthy value. Also, the returned value can be accessed upon awaiting the promise returned by the invocation of the wrapped function.

Usage

Install

npm install @dizmo/functions-queued --save

Require

const { queued } = require('@dizmo/functions-queued');

Examples (functions)

import { queued } from '@dizmo/functions-queued';

Dequeue with next (and auto=true):

const fn = queued(function fn(
    n: number, next: Function
) {
    setTimeout(next, 200);
});
fn(1); fn(2); const result = await fn(3);

Dequeue with next (and auto=false):

const qn = queued.auto(false)((
    ...args: any[]
) => {
    const next = args.pop() as Function;
    setTimeout(next, 200);
});
qn(1); qn(1, 2); qn(1, 2, 3); qn.next();

Dequeue with Promise (and auto=true):

const gn = queued(function gn(
    n: number
) {
    return Promise.resolve(true);
});
gn(1); gn(2); const result = await gn(3);

Dequeue with Promise (and auto=false):

const qn = queued.auto(false)((
    ...args: any[]
) => {
    return new Promise((resolve) => {
        setTimeout(() => resolve(true), 200);
    });
});
qn(1); qn(1, 2); qn(1, 2, 3); qn.next();

Examples (functions) with queue size

Dequeue with Promise (and size=0):

const gn = queued(function gn(
    n: number
) {
    return Promise.resolve(true);
}, {
    size: 0
});
gn(1); // throws QueueError('full')

Dequeue with Promise (and size=1):

const gn = queued(function gn(
    n: number
) {
    return Promise.resolve(true);
}, {
    size: 1
});
gn(1); // throws QueueError('full')

Dequeue with Promise (and size=2):

const gn = queued(function gn(
    n: number
) {
    return Promise.resolve(true);
}, {
    size: 2
});
gn(1); gn(2); gn(3); // throws QueueError('full')

Dequeue with Promise (and size=3):

const gn = queued(function gn(
    n: number
) {
    return Promise.resolve(true);
}, {
    size: 3
});
gn(1); gn(2); gn(3); gn(4); // throws QueueError('full')

Examples (functions) with locking

Dequeue with next (and auto=true) plus a lock:

const fn = queued(function fn(
    n: number, next: Function
) {
    setTimeout(next, 200);
}, {
    sync: true, // dequeue synchronously [default]
    lock: {
        // acquire (pseudo) lock [default]
        acquire: () => Promise.resolve(true),
        // release (pseudo) lock [default]
        release: () => Promise.resolve(true)
    }
});
fn(1); fn(2); const result = await fn(3);

Dequeue with Promise (and auto=false) plus a lock:

const qn = queued.auto(false)((
    ...args: any[]
) => {
    return new Promise((resolve) => {
        setTimeout(() => resolve(true), 200);
    });
}, {
    sync: false, // dequeue asynchronously [*not* default]
    lock: {
        // acquire (pseudo) lock [default]
        acquire: () => Promise.resolve(true),
        // release (pseudo) lock [default]
        release: () => Promise.resolve(true)
    }
});
qn(1); qn(1, 2); qn(1, 2, 3); qn.next();

Examples (class methods)

import { queued } from '@dizmo/functions-queued';

Dequeue with next (and auto=true):

class AClass {
    @queued.decorator
    public method(
        n: number, next?: Function
    ) {
        if (next) setTimeout(next, 200);
    }
}
const obj = new AClass();
obj.method(1); obj.method(2); const result = await obj.method(3);

Dequeue with next (and auto=false):

class BClass {
    @queued.decorator(false)
    public method(
        n: number, next?: Function
    ) {
        if (next) setTimeout(next, 200);
    }
}
const obj = new BClass();
obj.method(1); obj.method(2); obj.method(3); obj.method.next();

..where in TypeScript casting obj.method to any might be required to access the next method.

Dequeue with Promise (and auto=true):

class CClass {
    @queued.decorator
    public method(
        n: number
    ) {
        return Promise.resolve(true);
    }
}
const obj = new CClass();
obj.method(1); obj.method(2); const result = await obj.method(3);

Dequeue with Promise (and auto=false):

class DClass {
    @queued.decorator(false)
    public method(
        n: number
    ) {
        return Promise.resolve(true);
    }
}
const obj = new DClass();
obj.method(1); obj.method(2); obj.method(3); obj.method.next();

..where in TypeScript casting obj.method to any might be required to access the next method.

Development

Clean

npm run clean

Build

npm run build

without linting and cleaning:

npm run -- build --no-lint --no-clean

with UMD bundling (incl. minimization):

npm run -- build --prepack

with UMD bundling (excl. minimization):

npm run -- build --prepack --no-minify

Lint

npm run lint

with auto-fixing:

npm run -- lint --fix

Test

npm run test

without linting, cleaning and (re-)building:

npm run -- test --no-lint --no-clean --no-build

Cover

npm run cover

without linting, cleaning and (re-)building:

npm run -- cover --no-lint --no-clean --no-build

Documentation

npm run docs

Publish

npm publish

initially (if public):

npm publish --access=public

Copyright

© 2021 dizmo AG, Switzerland