Have ideas to improve npm?Join in the discussion! »

    @chocolatey/when
    TypeScript icon, indicating that this package has built-in type declarations

    1.2.0 • Public • Published

    when

    Build Status NPM Version

    NAME

    when - subscribe to an event before or after it's fired

    FEATURES

    • no dependencies
    • < 500 B minified + gzipped
    • fully typed (TypeScript)
    • CDN builds (UMD) - jsDelivr, unpkg

    INSTALLATION

    $ npm install @chocolatey/when
    

    SYNOPSIS

    import when from '@chocolatey/when'
    
    const onPageShow = when(done => {
        window.addEventListener('pageshow', done)
    })
    
    // callback
    onPageShow(event => addWidget(user))
    
    // promise
    onPageShow().then(([event]) => addWidget(user))

    DESCRIPTION

    when provides a way to register listeners for a function call before or after the function is called. This can be used to "pin" events (or other kinds of signal/notification) so that they can be consumed after they've fired.

    Why?

    Several events and notifications that are exposed as transient, fire-and-forget messages are better thought of as states, e.g. "ready" notifications for services, or lifecycle events for web pages. They tend to be fired once, and we often need to detect that they've fired after the fact. This usage is not supported by most event-emitter implementations, and handling it manually can involve fiddly imperative code which obscures the simple semantics.

    This module exports a function which allows these notifications to be pinned like "sticky" announcements on a message board or forum, rather than the blink-and-you-miss-it behavior of events, removing the timing-sensitivity that can make the event-based representation of these states inconvenient to use or unreliable.

    Why not?

    when provides a way to pin notifications when a framework or library doesn't provide a way to do that itself, e.g. when consuming events produced by most event-emitter implementations. But if you control/emit the notifications yourself, and want consumers to be able to subscribe to them after they've been published, this can be handled in the notifier itself, e.g. by using a library with support for pinned events such as fixed-event or ipc-event-emitter.

    TYPES

    The following types are referenced in the descriptions below.

    export type Callback = (done: <A extends any[]>(...args: A) => void) => void;
    export type ErrorHandler = (error: any) => void;
    export type Listener = <A extends any[]>(this: unknown, ...args: A) => void;
    
    export interface Subscribe {
        <A extends any[]>(): Promise<A & { this: unknown }>;
        (listener: Listener): () => boolean;
    };

    EXPORTS

    default

    • Type: (callback: Callback, onError?: ErrorHandler) => Subscribe
    import when from '@chocolatey/when'
    
    const onPageShow = when(done => {
        window.addEventListener('pageshow', done)
    })
    
    user.getData(url) // load data if not cached
    
    onPageShow(event => addWidget(user))

    when passes a delegating function to a callback and returns a function which registers listeners to be called when the delegate is called. Listeners are passed the same arguments and this value as the delegate and are executed asynchronously. Listeners registered after the delegate has been called are invoked immediately.

    unsubscribe

    When the returned function is passed a listener, it returns a function which can be used to unregister the listener if it hasn't already been called. The function returns true if the listener was successfully unregistered, or false otherwise.

    const unsubscribe = onPageShow(() => addWidget(user))
    
    // ...
    
    if (!user.loggedIn) {
        unsubscribe()
    }

    promise

    If the listener is omitted, a promise is returned which is resolved with the array of arguments passed to the delegate.

    onPageShow().then(([event]) => addWidget(user))
    
    const [event] = await onPageShow()

    The delegate's this value can be accessed via the this property on the arguments array:

    onPageShow().then(args => {
        const [event] = args
        const self = args.this
    })
    
    const { 0: event, this: self } = await onPageShow()

    error handler

    when takes an optional error-handler which is passed any error which occurs when a listener is invoked. If not supplied, listener errors are logged with console.error.

    const onReady = when(
        done => emitter.once('ready', done),
        error => errors.push(error)
    )
    
    onReady(() => loadFile(path))
    // errors.push(new Error("no such file..."))

    once

    The delegate function records its arguments and this value the first time it's called and subsequent calls are ignored, i.e. it behaves like it's only called once. This makes it safe to use with events which may be emitted multiple times, though arranging for the delegate to only be called once can still be useful to avoid spamming it with redundant calls.

    const onBeforeUnload = when(done => {
        window.addEventListener('beforeunload', done, { once: true })
    })

    side effects

    Note that the delegating function is void, i.e. there's no way to return a value from it, and no way to return a different value from it each time it's called (since it's only executed once). There's also no way to perform a side effect such as stopping the propagation of an event, since listeners are executed asynchronously. If any of these features are needed, they can be handled by wrapping the delegate, e.g.

    const onReady = when(done => {
        const wrapper = function (event) {
            const result = handle(event) // side effects
            done.call(this, event)       // notify listeners (once)
            return result                // return value
        }
    
        emitter.on('ready', wrapper)
    })

    DEVELOPMENT

    NPM Scripts

    The following NPM scripts are available:

    • build - compile the library for testing and save to the target directory
    • build:doc - generate the README's TOC (table of contents)
    • build:release - compile the library for release and save to the target directory
    • clean - remove the target directory and its contents
    • rebuild - clean the target directory and recompile the library
    • test - recompile the library and run the test suite
    • test:run - run the test suite
    • typecheck - sanity check the library's type definitions

    COMPATIBILITY

    SEE ALSO

    • fixed-event - EventEmitter for one-time tasks
    • ipc-event-emitter - an EventEmitter wrapper for IPC between parent and child processes with support for pinned events
    • wl - whenable events implementation

    VERSION

    1.2.0

    AUTHOR

    chocolateboy

    COPYRIGHT AND LICENSE

    Copyright © 2021 by chocolateboy.

    This is free software; you can redistribute it and/or modify it under the terms of the MIT license.

    Install

    npm i @chocolatey/when

    DownloadsWeekly Downloads

    117

    Version

    1.2.0

    License

    MIT

    Unpacked Size

    24.9 kB

    Total Files

    12

    Last publish

    Collaborators

    • avatar