🔮 Helpers for things that happen in the future.
- Event listener utilities that provide cleanup functions
- Timer helpers with convenient registering and cleanup functions
- Promise generators that wait for event occurrences
- Tool to delay function calls with throttling and introspection capabilities
Everything is exported from the main entry-point through an ES6 module:
import {
EventEmitter,
on,
once,
timeout,
interval,
delay,
until,
untilOnline,
reduceStatusList,
} from "futurise";
Install with the Node Package Manager:
npm install futurise
Documentation is generated here.
Use EventEmitter
to create a new event emitter:
import { EventEmitter } from "futurise";
const emitter = new EventEmitter<{
A: boolean;
B: number;
}>();
emitter.dispatchEvent("A", true);
emitter.dispatchEvent("B", 4);
It follows the EventTarget interface for adding and removing listeners:
function listener(event: boolean) {
console.log(`Boolean value: ${event}`);
}
emitter.addEventListener("A", listener);
emitter.removeEventListener("A", listener);
Use on
to register a listener on an element
or any object that has the addEventListener
and removeEventListener
methods of the EventTarget
interface:
import { on } from "futurise";
const off = on(element, "click", () => console.log("Clicked!"));
It returns a function that, when called, removes the listener
from the element
:
off();
This is convenient when used with tools that rely on cleanup functions such as the React
hook useEffect
:
import { on } from "futurise";
import { useEffect } from "preact/hooks";
useEffect(() => on(element, "click", () => console.log("Clicked!")), [element]);
The on
register function can be curried by passing only the target and event name:
import { on } from "futurise";
const register = on(element, "click");
const off = register(() => console.log("Clicked!"));
const anotherOff = register(() => console.log("Another message!"));
This is convenient when using with until
, a tool that returns a promise that resolves when a specific event is spotted:
import { until, on } from "futurise";
async function untilOnline(request) {
await until(on(window, "online"));
return request();
}
Note that futurise
exports a tool untilOnline
that resolve to true
if it waited for the system to become online:
import { untilOnline } from "futurise";
Similar to on
, once
registers a listener for a given event
on a target
only once:
import { once } from "futurise";
// Will execute the callback only once
once(element, "click", () => console.log("Clicked!"));
Use timeout
to execute a macro-task after a given delay:
import { timeout } from "futurise";
const cancel = timeout(1000, () => console.log("At least a second elapsed!"));
It returns a function that, when called, cancels the timer if it has not yet elapsed:
cancel();
Like for the on
tool, this is convenient when used with tools that rely on cleanup functions such as the React
hook useEffect
:
import { timeout } from "futurise";
import { useEffect } from "preact/hooks";
useEffect(
() => timeout(1000, () => console.log("At least a second elapsed!")),
[],
);
The timeout
register function can be curried by passing only the duration
. Similar to currying on
, it returns a register function enabling several callbacks to be registered and called once the duration elapses. Note that calling timeout
immediately sets the timer:
import { timeout } from "futurise";
const register = timeout(1000);
register(() => console.log("At least a second elapsed!"));
register(() => console.log("This is called immediately after!"));
const unregister = register(() =>
console.log("But not this, because it will be deregistered"),
);
unregister();
// After about one second, the console will show:
// > At least a second elapsed!
// > This is called immediately after!
Unregistering all callbacks clears the timeout if it did not elapse:
import { timeout } from "futurise";
const register = timeout(1000);
const unregister1 = register(() => console.log("At least a second elapsed!"));
const unregister2 = register(() => console.log("Yes, at least!"));
unregister1();
unregister2();
// The timeout is cleared
As for on
, this is convenient when used together with until
:
import {timeout, until} from "futurise";
async function task() {
let done = false;
while (!done) {
try {
await action();
done = true;
} except (error) {
console.error(error);
await until(timeout(1000));
}
}
}
Note that futurise
exports a tool sleep
that combines until
and timeout
:
import { sleep } from "futurise";
async function task() {
await sleep(1000);
}
Use interval
to repetitively execute a macro-task after a given interval:
import { interval } from "futurise";
const cancel = interval(1000, () => console.log("At least a second elapsed!"));
It returns a function that, when called, cancels the interval:
cancel();
The interval
register function can be curried by passing only the duration
. Similar to currying on
, it returns a register function enabling several callbacks to be registered and called at each interval. Note that calling interval
immediately sets the interval:
import { interval } from "futurise";
const register = interval(1000);
register(() => console.log("At least a second elapsed!"));
register(() => console.log("This is called immediately after!"));
const unregister = register(() =>
console.log("But not this, because it will be deregistered"),
);
unregister();
// After about one second, the console will show:
// > At least a second elapsed!
// > This is called immediately after!
// After about one second later, the console will show:
// > At least a second elapsed!
// > This is called immediately after!
// And so on…
Unregistering all callbacks clears the interval:
import { interval } from "futurise";
const register = interval(1000);
const unregister1 = register(() => console.log("At least a second elapsed!"));
const unregister2 = register(() => console.log("Yes, at least!"));
unregister1();
unregister2();
// The interval is cleared
Delaying the invocation of a callable (also known as "debouncing") can be done using the delay
tool:
import { delay } from "futurise";
function doSomething(parameter) {
heavyTaskWith(parameter);
}
const doSomethingLessOften = delay(1000, doSomething);
doSomethingLessOften(1);
doSomethingLessOften(2);
doSomethingLessOften(3);
doSomethingLessOften(4);
// Only calls `doSomething(4)` after 1 second
By default, the function is called after the specified duration elapsed. The immediate
option enables calling the function immediately:
import { delay } from "futurise";
function doSomething(parameter) {
heavyTaskWith(parameter);
}
const doSomethingLessOften = delay(1000, doSomething, { immediate: true });
// Calls `doSomething(1)` immediately
doSomethingLessOften(1);
doSomethingLessOften(2);
doSomethingLessOften(3);
doSomethingLessOften(4);
// Calls `doSomething(4)` after 1 second
By default, the function is delayed until the duration elapsed before a new call is made. The throttle
option enables calling the function at most once every period set by the duration
:
import { delay, sleep } from "futurise";
function doSomething(parameter) {
heavyTaskWith(parameter);
}
const doSomethingLessOften = delay(1000, doSomething, { throttle: true });
async function task() {
doSomethingLessOften(1);
await sleep(400);
doSomethingLessOften(2);
await sleep(400);
doSomethingLessOften(3);
await sleep(400); // Calls `doSomething(3)` while it awaits the sleep timer
doSomethingLessOften(4);
await sleep(1000); // Calls `doSomething(4)` while it awaits the sleep timer
}
Without the throttling mode, the function would have been called only once with doSomething(4)
.
The function returned by delay
has a cancel()
method that cancel the pending invocation:
import { delay } from "futurise";
function doSomething(parameter) {
heavyTaskWith(parameter);
}
const doSomethingLessOften = delay(1000, doSomething);
doSomethingLessOften(1);
doSomethingLessOften.cancel();
// The function is not called
The function returned by delay
has a flush()
method that immediately runs the pending invocation and returns the resulting value:
import { delay } from "futurise";
function doSomething(parameter) {
// Returns something
return heavyTaskWith(parameter);
}
const doSomethingLessOften = delay(1000, doSomething);
doSomethingLessOften(1);
// The function is immediately called and its result is returned
const result = doSomethingLessOften.flush();
The function returned by delay
has a result
property that is set to the value returned by the function:
import { delay } from "futurise";
function doSomething(parameter) {
// Returns something
return heavyTaskWith(parameter);
}
// Setting `immediate: true` to immediately execute the first invocation:
const doSomethingLessOften = delay(1000, doSomething, { immediate: true });
// The function is immediately called
doSomethingLessOften(1);
// The result can be retreived as such
const result = doSomethingLessOften.result;