Skip to main content
Deno 2 is finally here 🎉️
Learn more

promise_fns

Promise utility functions for Deno.

main semantic-release

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

src test

// 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.

src test

import allLazy from "./src/allLazy.ts";
console.log(
  await allLazy([() => countFiles("test"), () => testIsFile("test")])
);
[ 19, false ]

catchIf

conditional promise catch handler

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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

src test

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