nonsynchronous

1.2.0 • Public • Published

nonsynchronous

async/await callback fusioning utilities

Functions

Promisify

Implementation:

const { promisify } = require('util')

The same promisify as provided in Node core.

Example:

const { promisify } = require('nonsynchronous')
const fs = require('fs')
const stat = promisify(fs.stat)
async function run () {
  console.log(await stat('.'))
}
run()

Once

Implementation:

const once = require('events.once')

The same once as provided in Node core (also polyfilled for earlier Node 10 versions).

Example:

const { once } = require('nonsynchronous')
const http = require('http')
async function run () {
  const server = http.createServer()
  server.listen()
  await once(server, 'listening')
  console.log(server.address())
}
run()

When

Useful for when you want to explicitly use a callback based API within an async function. The function returned from when must be called after awaiting the done method on that function other wise an error called before awaiting done() will be thrown.

Implementation:

const when = () => {
  var done = () => { throw Error('called before awaiting done()') }
  const fn = () => done()
  fn.done = promisify((cb) => { done = cb })
  return fn
}

Example:

const { when } = require('nonsynchronous')
async function run () {
  const until = when()
  setTimeout(() => {
    until()
  }, 1000)
  await until.done()
  console.log('timeout complete')
}
run()

Whenify

Useful for using a callback based API within an async function without any modifications required within the callback itself.

Must be used with a callback last API (for callback first, just use when manually).

Used in conjunction with the done symbol and optionally the count symbol. The asyncOps option is explored in the count symbol documentation.

Implementation:

const whenify = (fn, { asyncOps } = { asyncOps: 1 }) => {
  const until = when()
  const max = asyncOps - 1
  const result = (...args) => {
    const cb = args.pop()
    return fn(...args, (...args) => {
      cb(...args) // eslint-disable-line
      if (++result[count] > max) until()
    })
  }
  result[count] = 0
  Object.defineProperty(result, done, {
    get () { return until.done() }
  })
  return result
}

Example:

const { whenify, done } = require('nonsynchronous')
const fs = require('fs')
async function run () {
  const stat = whenify(fs.stat)
  stat('.', (err, stats) => {
    console.log(err, stats)
  })
  await stat[done]
}
run()

Whenify Method

Whenify an object method so that method[done] can be awaited (where done is the done symbol). This can be particularly useful when writing tests for an instance with callback based methods. The test be both self documenting and async compatible.

Implementation:

const whenifyMethod = (instance, method, opts) => {
  const result = whenify(instance[method].bind(instance), opts)
  instance[method] = result
  return instance
}

Example:

const { whenifyMethod, done } = require('nonsynchronous')
const fs = require('fs')
whenifyMethod(fs, 'stat')
async function run () {
  fs.stat('.', (err, stats) => {
    console.log(err, stats)
  })
  await fs.stat[done]
}
run()

Promisify Method

Convert a callback-based method on an instance into its promisifed equivalent. Can be passed multiple method names to easily promisify entire instances.

Implementation:

const _promisifyMethod = (instance, method) => {
  const result = promisify(instance[method])
  instance[method] = result
  return instance
}
const promisifyMethod = (instance, ...methods) => {
  methods.forEach(_promisifyMethod.bind(null, instance))
  return instance
}

Example:

const { promisifyMethod } = require('nonsynchronous')
const http = require('http')
async function run () {
  const server = http.createServer()
  promisifyMethod(server, 'listen')
  await server.listen()
  console.log(server.address())
}
run()

Promisify Of

Partially apply a promisify of a particular method on any given instance without mutating the instance. This is useful as an alternative to promisifyMethod if you don't want to overwrite instance keys and/or you have multiple different instances that you want to promisify the same method of.

Implementation:

const promisifyOf = (method) => {
  return (instance) => promisify((...args) => instance[method](...args))
}

Example:

const { promisifyOf } = require('nonsynchronous')
const http = require('http')
async function run () {
  const server = http.createServer()
  const listen = promisifyOf('listen')
  await listen(server)(2000) // listen on port 2000
  console.log(server.address())
}
run()

Immediate

Promisified setImmediate.

Implementation:

const immediate = promisify(setImmediate)

Example:

const { immediate } = require('nonsynchronous')
async function run () {
  console.log('this tick')
  await immediate()
  console.log('next tick')
}
run()

Timeout

Promisified setTimeout.

Implementation:

const timeout = promisify(setTimeout)

Example:

const { timeout } = require('nonsynchronous')
async function run () {
  await timeout(1000)
  console.log('timeout complete')
}
run()

Symbols

Done

The done symbol is used with whenify and whenifyMethod to access a promise on the function that has been whenified which can be awaited. The promise will complete once the callback for a whenified function has been called.

Count

The count symbol is used with whenify and whenifyMethod to access a the total amount of times a whenified functions callback was called. This is only useful when you set the asyncOps option to greater than 1.

const { whenify, done, count } = require('nonsynchronous')
const multiCallbackThing = whenify(
  require('multi-callback-thing'),
  {asyncOps: 3} // expecting three calls of the callback
)
async function run () {
  multiCallbackThing(() => {
    // how many times has this cb been called so far?
    // note: the count is zero indexed.
    console.log(`called ${multiCallbackThing[count] + 1} times`) 
  })
}
run()

Custom Promisify Args

The customPromisifyArgs symbol exposes a Node core symbol used to alter promisify behaviour for callbacks then have extra arguments.

const { promisify, customPromisifyArgs } = require('nonsynchronous')
const multiArgApi = (cb) => setImmediate(cb, null, 'circle', 'red')
multiArgApi[customPromisifyArgs] = ['shape', 'color']
const multiValApi = promisify(multiArgApi)
 
async function run () {
  const { shape, color } = await multiValApi()
  console.log(`shape: ${shape}\ncolor: ${color}`)
}
run()

Behaviors

Automatically includes and enable make-promisies-safe which causes Node to treat unhandled rejections as unhandled exceptions (e.g. throws and exits).

Tests & Coverage

npm test
Suites:   1 passed, 1 of 1 completed
Asserts:  24 passed, of 24
----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.js |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|

License

MIT

Readme

Keywords

Package Sidebar

Install

npm i nonsynchronous

Weekly Downloads

533

Version

1.2.0

License

MIT

Unpacked Size

14.3 kB

Total Files

5

Last publish

Collaborators

  • davidmarkclements