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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@lbfalvy/when

v4.1.1

Published

Everything you were missing from Promises

Downloads

36

Readme

When

The core API mimics the promise API, but it has a couple extensions.

Lazy

By default all promises in when are lazy, that is, the executor doesn't run until the first then call. This can save you bandwidth or other resources and it allows you to space out your network requests without the help of a separate data fetching library. If you need an eager promise, use when.eager. If the executor has side effects, consider using the [[#Exposed]] form instead.

Synchronous mode

then has a third optionsl argument, which is the string 'sync'. If this is present, both fulfill and reject handlers will be called synchronously. Use this sparingly, timing sensitivity is generally not a good thing. Catch and finally both also have this argument. You can also pass 'sync' to when, both in the flat and executor-based formats, to apply the switch to all "then" calls. This is useful for unit testing.

promise.then(result => {}, err => {}, 'sync')
const [promise, resolve, reject, cancel] = when('sync')

Cancelable promises

The executor can return a function which will be called on cancellation.

const promise = when((resolve, reject) => {
	const request = someComplicatedProcess()
	request.on('complete', data => resolve(data))
	request.on('error', err => reject(err))
	return () => request.cancel()
})

The promises returned by when() have a cancel function, which can be called to invoke this particular executor's cancel handler.

promise.cancel()

cancel can also be passed a callback that will receive cancel itself. This is so that you can embed it in promise chains.

// cancelation with mini-events
const [cancel, addCancel] = event()
longOperation()
	.cancel(addCancel)
	.then(result => {}, err => {})
// It is really just a function, you can do with it whatever you want.
const cbs = new Set<() => void>
const addCB = cb => cbs.add(cb)
let cancel = cbs.forEach(f => f())
promise.cancel(addCB).then(result => {}, err => {})
// The possibilities are endless
promise.cancel(when(2000).then).then(result => {}, err => {})
// A good use case may be to connect to the first available of several servers.
const promises = ['server1', 'server2', 'server3'].map(tryConnect)
const either = when.any(promises)
promises.forEach(p => p.cancel(either.then))

Cancellation is never propagated on the Promise chain, only the last executor's canceller is invoked. Result handlers are also not invoked, because if you cancel a promise you likely control all of its handlers.

Readable state

The state is indicated on the instance and the return value can be synchronously obtained.

promise.state == 'fulfilled'|'rejected'|'pending'|'cancelled'
promise.value == ...
promise.error == ...
promise.settle // getter returns another promise

Exposed

If an executor is not provided, the factory returns its parameters in a tuple with the instance. In this case, cancelation is represented as a subscribe function.

const [promise, resolve, reject, cancelation] = when()

Promise wrapper

If you pass a thenable to when with no parameters, it is wrapped in a when-promise.

const promise = when(fetch('foo.png'))

Timer

If the executor is replaced with a number, the promise will resolve after as many milliseconds.

// Wait 1 second
await when(1000)

Events

If when is called with an object and one or two event names, it attaches resolve and reject to the events. The following pairs of functions are looked up on the object, and the first to exist is used as it would normally be used in its corresponding environment to attach and detach handlers.

  • addEventListener/removeEventListener (browser)
  • addListener/removeListener (node)
  • on/off (concise node)

If none are found, an error is thrown.

const clickEvent = when(document.getElementById('mybtn'), 'click')

Callbacks

To promisify standard node callbacks where the first argument is error and the second is the result, you can pass the function and its arguments to when.cb

const settings = await when.cb(readFile, './settings.json')

Tapped function calls

Detect when a function is called. Useful if you need to observe behavior for which an event is not provided. Note that this is capable of violating the interface of whatever object you're using it on. Be very careful and always double-check if what you're tapping into can be accessed in some other, legitimate way.

when.tap takes an object and one or two indices, and assigns handlers to the indices. It returns a thenable which is resolved when the first or rejected when the second index is called.

When the promise is settled, the indices are reset to their previous value. If they contained a function and the reason for disengagement was a call rather than cancellation, the call is relayed to that function.

const firstMessage = await when.tap(port, 'postMessage', 'close')

Aggregators, defaults

All the capabilities of when are available with the classic promise helpers.

when.resolve(data) // Promise.resolve
when.reject(error) // Promise.reject
when.cancel()
when.all([new Promise(), when(foo)]) // Promise.all
when.any([new Promise(), when(foo)]) // Promise.any
when.allSettled([new Promise(), when(foo)]) // Promise.allSettled