pext

Promise Extensions

Usage no npm install needed!

<script type="module">
  import pext from 'https://cdn.skypack.dev/pext';
</script>

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.