npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pext

v0.8.0

Published

Promise Extensions

Readme

pext Promise Extensions

Gitter Chat Build Status


import {deadline, asyncMap} from 'pext'

// query user ids (redis style)
const users = await db.keys('users:*')
// for every user id - query user data
::asyncMap(key => db.get(key))
// parse JSON
::asyncMap(JSON.parse)
// throw if it takes longer than 3 seconds
::deadline(3000)
// if it failed, rethrow with a more descriptive message
::rethrow("Couldn't load user data")

console.log(users)
// [
//   {name: '…', email: '…'},
//   {name: '…', email: '…'},
//   …
// ]

API

delay(time)

Returns a promise that resolves in time milliseconds counted from the moment of calling the function or - if called on a promise - from the moment the original promise resolves.

console.log("Hello")
await delay(1000)
console.log("world.")

const json = await getJSON('…')::delay(1000).then(JSON.parse)

Promise::deadline(time, message?)

Returns a promise that either resolves normally or rejects if it takes longer than time milliseconds after calling the function. message is the error message ("Operation timed out" by default). Consider chaining ::rethrow() after it instead of specifying message.

const json = await fetch('…').then(res => res.json())::deadline(1000)

Promise::rethrow(message)

Catches a rejected promise and rethrows it with more descriptive error.

const cfg = await promisify(fs.readFile)('config.json', 'utf8')
.then(JSON.parse)
::deadline(1000)
::rethrow("Can't load config")
// Can't load config: No such file or directory
// Can't load config: Invalid JSON
// Can't load config: Operation timed out

Function::retry(n)

Calls a function returning a promise multiple times (n max) until it resolves successfully

const json = await ( () => fetch('/data.json') )::retry(3).then(r => r.json())

Extra arguments are passed to the function so you can use this method directly on the function.

const json = await fetch::retry(3, '/data.json').then(r => r.json())

Array::asyncMap(elem => newValue, {concurrency})

Returns a promise for an array of new values. newValue can be a promise. concurrency is the maximum number of promises pending at once (Infinity by default, 1 = sequential execution).

It can be called on a regular array as well as on a promise for an array, an array of promises or a promise for an array of promises.

See also Array.prototype.map on MDN.

const jsons = await ['a.json', 'b.json']
::asyncMap(f => promisify(fs.readFile)(f, 'utf8'))
::asyncMap(JSON.parse)

Array::asyncReduce((lastVal, elem) => newVal, initVal?)

Returns a promise for the final accumulator value (final newVal). newVal and initVal can be promises.

It can be called on a regular array as well as on a promise for an array, an array of promises or a promise for an array of promises.

See also Array.prototype.reduce on MDN.

const poem = ['verse1.txt', 'verse2.txt']
// it would be more wise to first map them to their contents
::asyncReduce(async (poem, file) => {
  const verse = await promisify(fs.readFile)(file, 'utf8')
  return poem + verse
}, '')

Array::asyncFlatten()

Equivalent to ::asyncReduce((acc, el) => acc.concat(el), [])

Array::asyncFlatMap(elem => newValue)

Equivalent to ::asyncMap(elem => newValue)::asyncFlatten()

Array::asyncUnique(elem => compareVal)

Returns promise for an array with duplicate values removed. The mapping function is optional - when it's specified, it uses the map result (compareVal) for comparison. compareVal can be a promise.