promise_fns
Promise utility functions for Deno.
Inspired by the sindresorhus
promise-fun collection. Parity
with the node promise-fun
library is not guaranteed.
Features:
- functions are independently importable
- type safe, type reflecting APIs
- concurrency control for most collection iterating functions
- debuggable. simplified source, named functions (so your stack trace is legible!)
Usage
You can import any individual function as follows:
import fn from "https://deno.land/x/promise_fns/src/FUNCTION_NAME.ts";
fn(...)
All functions are listed in the functions table.
A full example:
import delay from "https://deno.land/x/promise_fns/src/delay.ts";
const delayedTwo = await delay(100, 2);
console.log(1 + delayedTwo); // 3
Functions
all
resolve a collection of promises. it’s just Promise.all
// eager
import all from "./src/all.ts";
console.log(await all([countFiles("."), testIsFile("readme.md")]));
[ 16, true ]
allLazy
resolve a lazy collection of promises. that is, execute and resolve a collection of thunks that return promises.
import allLazy from "./src/allLazy.ts";
console.log(
await allLazy([() => countFiles("test"), () => testIsFile("test")])
);
[ 19, false ]
catchIf
conditional promise catch handler
import catchIf from "./src/catchIf.ts";
const useZeroOnMissing = catchIf(
(err) => !!err?.message.match(/No such file/),
() => 0
);
const filesInBogusDir = await countFiles("/bogus/directory")
.then(console.log)
.catch(useZeroOnMissing);
console.log({ filesInBogusDir });
{ filesInBogusDir: 0 }
delay
create a promise that sleeps for some milliseconds then resolves
import delay from "./src/delay.ts";
const delay5 = delay(5).then(() => console.log(5));
const delay1 = delay(1).then(() => console.log(1));
const delay3 = delay(3).then(() => console.log(3));
await all([delay5, delay1, delay3]);
1
3
5
delayReject
create a promise that sleeps for some milliseconds then rejects
import delayReject from "./src/delayReject.ts";
const delayReject5 = delayReject(5, new Error(`${5}`)).catch(console.error);
const delayReject1 = delayReject(1, new Error(`${1}`)).catch(console.error);
const delayReject3 = delayReject(3, new Error(`${3}`)).catch(console.error);
await all([delayReject5, delayReject1, delayReject3]);
Error: 1
at file:///home/runner/work/promise_fns/promise_fns/readme.ts:20:37
Error: 3
at file:///home/runner/work/promise_fns/promise_fns/readme.ts:21:37
Error: 5
at file:///home/runner/work/promise_fns/promise_fns/readme.ts:19:37
event
await an event completion in a promise
import event from "./src/event.ts";
import { EventEmitter } from "./test/fixture/3p.ts";
class TestEmitter extends EventEmitter<{ foo: [string] }> {}
const emitter = new TestEmitter();
const resolvesOnEvent = event(emitter, "on", "foo");
emitter.emit("foo", "bar");
console.log({ emittedValue: await resolvesOnEvent });
{ emittedValue: [ "bar" ] }
if
conditional promise chains
import pIf from "./src/if.ts";
console.log({
youReceived: await Promise.resolve("big money!").then(
pIf(
Math.random() > 1 / 1e9,
(prize) => `congrats, ${prize}`,
() => "sorry, zero bucks :("
)
),
});
{ youReceived: "congrats, big money!" }
log
log the resolved value of a promise
import createPromiseLogger from "./src/log.ts";
const pLog = createPromiseLogger<number>(console.info);
await Promise.resolve(1)
.then(pLog)
.then((v) => v + 1)
.then(pLog);
1
2
logCatch
Log the rejected value of a promise
import createLogCatch from "./src/logCatch.ts";
await Promise.reject(new Error("something terrible has happened"))
.catch(createLogCatch())
.catch(() => null);
Error: something terrible has happened
at file:///home/runner/work/promise_fns/promise_fns/readme.ts:43:22
map
maps a collection into a new collection asynchronously
import map from "./src/map.ts";
const mapped = await map(["readme.md", "tacos.now"], testIsFile);
console.log(mapped);
[ true, false ]
promisify
converts a node-style callback function into a promisified function, returning
the result after err
, per (err, result) => void
import promisify from "./src/promisify.ts";
type OldSkoolFn = (cb: (err: Error | null, result: string) => void) => void;
const cbStyleFn: OldSkoolFn = (cb) => cb(null, "yippy. skippy.");
const coolFn = promisify(cbStyleFn);
console.log(await coolFn());
yippy. skippy.
promisifyMulti
converts a node-style callback function into a promisified function, returning
all results after err
, per (err, ...results) => void
import promisifyMulti from "./src/promisifyMulti.ts";
type OldSkoolFnMulti = (
cb: (err: Error | null, ...results: string[]) => void
) => void;
const cbStyleMultiFn: OldSkoolFnMulti = (cb) => cb(null, "uno", "dos", "tres");
const coolMultiFn = promisifyMulti(cbStyleMultiFn);
console.log(await coolMultiFn());
[ "uno", "dos", "tres" ]
props
maps a { key:promise } mapped collection to a { key:resolved-promise }
mapped
collection
import props from "./src/props.ts";
console.log(
await props({
pbAndJ: delay(1).then(() => "incredible"),
subway: delay(3).then(() => "legally not bread in ireland"),
cubano: delay(0).then(() => "oooooh baby!"),
}).then((result) => `PB and J is ${result.pbAndJ}`)
);
PB and J is incredible
queue
creates a queue that allows users to add work. queue resolves when no work is outstanding
import createQueue from "./src/queue.ts";
const plannedWork = [5, 0, 3];
const { add, queue, subscribe } = createQueue<number>({ concurrency: 1 });
subscribe((v) => console.log(`completed work: ${v}`));
for (const ms of plannedWork) {
console.log(`adding work ${ms} to queue`);
const work = () => delay(ms).then(() => ms);
add(work);
}
const result = await queue;
adding work 5 to queue
adding work 0 to queue
adding work 3 to queue
completed work: 5
completed work: 0
completed work: 3
tap
Tap into a resolving promise chain without affecting the resolved value
import tap from "./src/tap.ts";
await countFiles(".").then(tap((n) => console.log(`found ${n} files`)));
found 16 files
tapCatch
tap into a rejecting promise chain without affecting the rejected value
import tapCatch from "./src/tapCatch.ts";
await countFiles("/bogus")
.catch(tapCatch(() => console.error(`count files failed, but its all good`)))
.catch(() => 0);
count files failed, but its all good
More functions are on their way!
Demo support functions
The demo functions used in the above demos are defined as follows:
async function countFiles(dirname: string) {
let i = 0;
for await (const _ of Deno.readDir(dirname)) ++i;
return i;
}
function testIsFile(filename: string): Promise<boolean> {
return Deno.stat(filename)
.then(({ isFile }) => isFile)
.catch(() => false);
}
contributing
Wanting to add more great stuff?. See .github/contributing.md