⚡🔒 Generate functions that do not allow parallel executions 🔒 ⚡
Let you create functions that enforce no more than one execution happens at the same time.
If the function is called again while there is already an execution ongoing the new call will be queued and executed once all the queued calls have completed.
This is a higher-level approach to the problem addressed by DirtyHairy/async-mutex
.
While being fitted for a smaller set of use-cases, this library is way less verbose and much easier to use than async-mutex
is.
Browserify friendly:
- No polyfills needed ✅
- Transpiled down to ES3 ✅
- Ultra light ✅
Usage
Let us compare a run-exclusive function with a regular function.
Regular function
let alphabet= "";
async function spell(letter: string): Promise<string>{
await new Promise<void>(resolve=> setTimeout(resolve, Math.random()*100));
alphabet+=letter;
return alphabet;
}
spell("a");
spell("b");
spell("c").then( message => console.log(message));
Run exclusive function
import * as runExclusive from "run-exclusive";
let alphabet= "";
const spell= runExclusive.build(
async (letter: string): Promise<string> => {
await new Promise<void>(resolve=> setTimeout(resolve, Math.random()*100));
alphabet+=letter;
return alphabet;
}
);
spell("a");
spell("b");
spell("c").then( message => console.log(message));
The types definition of the function passed as argument are conserved.

Sharing a unique lock among a group of functions
import * as runExclusive from "run-exclusive";
let alphabet= "";
const groupSpelling= runExclusive.createGroupRef();
const spellUpperCase= runExclusive.build(groupSpelling
async (letter: string) => {
await new Promise<void>(resolve=> setTimeout(resolve, Math.random()*100));
alphabet+=letter.toUpperCase();
}
);
const spellLowerCase= runExclusive.build(groupSpelling
async (letter: string) => {
await new Promise<void>(resolve=> setTimeout(resolve, Math.random()*100));
alphabet+=letter.toLowerCase();
}
);
spell("a");
spellUpperCase("b");
spell("c").then(()=> console.log(alphabet));
Defining class method
If you define run exclusive class methods chances are you want the lock to be restricted
to the class's object instance.
This is what buildMethod()
is for.
class Student {
public alphabet= "";
public spell= runExclusive.buildMethod(
async (letter: string) => {
await new Promise<void>(resolve=> setTimeout(resolve, 1000));
this.alphabet+=letter.toLowerCase();
}
);
}
let alice= new Student();
let bob= new Student();
alice.spell("A");
bob.spell("a");
alice.spell("B");
bob.spell("b");
alice.spell("C").then( ()=> console.log(alice.alphabet));
bob.spell("c").then( ()=> console.log(bob.alphabet));
Using callback instead of promises.
buildCb()
is the pending of build()
for creating run exclusive functions that complete by invoking a callback. (Instead of resolving a promise).
The only valid reason to use this instead of build()
is to be able to retrieve the result synchronously.
WARNING: The function should never throw as the exception wont be catchable.
let alphabet= "";
const spell= runExclusive.buildCb(
(letter: string, callback?: (message: string)=> void) => {
setTimeout(()=>{
alphabet+= letter;
callback!(alphabet);
}, Math.rand()*100);
}
};
spell("a");
spell("b");
spell("c", message => console.log(message));
Checking the queuedCalls of a run exclusive function
It is possible to check, for a given run exclusive function, if it is currently
an ongoing execution and how many calls are queued.
It is also possible to cancel the queued calls.
export declare function getQueuedCallCount(runExclusiveFunction: Function, classInstanceObject?: Object): number;
export declare function cancelAllQueuedCalls(runExclusiveFunction: Function, classInstanceObject?: Object): number;
export declare function isRunning(runExclusiveFunction: Function, classInstanceObject?: Object): boolean;
export declare function getPrComplete(runExclusiveFunction: Function, classInstanceObject?: Object): Promise<void>;