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

easy-defer

v1.0.1

Published

A simple promise-based defer and synchronisation mechanism

Downloads

5

Readme

easy-defer

Build Status Dependency Status Code Coverage

A simple promise-based defer and synchronisation mechanism

The Defer class is basically a Promise that makes resolve(), reject() and the state available externally.

Basic Usage

const Defer = require('easy-defer').default;

const d1 = new Defer();
d1.then(() => console.log('d1 completed'));

// elsewhere
d1.resolve();

API

Importing

Note that when importing using commonjs require, you must specify .default:

const Defer = require('easy-defer').default;

You may also use import:

import Defer from '../src/Defer';

The Defer Class

Properties

As with Promise, Defer can be in one of three states, but unlike Promise, those states can be observed externally:

  • isFulfilled - completed successfully (resolve() was called)
  • isPending - not completed either way (inverse of isSettled)
  • isRejected - completed unsuccessfully (reject() was called)
  • isSettled - completed either successfully or unsucessfully

Methods

Most of the methods are exactly the same as in Promise, with the exception that resolve() and reject() would not normally be available externally. All the parameters are optional.

catch(onRejected)

Defines a function to be called if the Defer is rejected. onRejected receives one parameter, reason, which is whatever was passed to reject().

finally(onFinally)

Defines a function to be called when the Defer settles, regardless of whether it is resolved or rejected. onFinally receives no parameters.

reject(reason)

Causes the Defer to settle in the rejected state.

resolve(value)

Causes the Defer to settle in the fulfilled state.

then(onFulfilled[, onRejected])

Defines function(s) to be called when the Defer settles. onFulfilled is called if the Defer was resolved, it receives a parameter of the fulfillment value (whatever was passed to resolve()). onRejected is equivalent to using catch().

toString()

Behaves exactly like Promise, returning the string 'Promise'.

Advanced Usage

The Defer object is a Promise so it can take part in Promise-like activities.

To check whether a series of events have occurred:

const d2 = new Defer();
const d3 = new Defer();
Promise.all([d2, d3])
    .then(() => console.log('d2, d3 complete'))
    .catch((err) => console.log(err));

// elsewhere
d2.resolve();

// another place
d3.resolve();

Given the primary use cases, the promises will generally be fulfilled. The reject() method is included for completeness and to allow error propagation:

const d4 = new Defer();
const d5 = new Defer();
Promise.all([d4, d5])
    .then(() => console.log('d4, d5 complete'))
    .catch((err) => console.log(err));

// elsewhere
d4.resolve();

// another place
d5.reject('d5 did not work');

Examples

Testing EventEmitter

The original use case for Defer was testing an EventEmitter as a black box.

Tests sometimes need to check that several events have been emitted. Usually we try to constrain what happens using mock dependencies, but that is not always possible (or desirable). By creating several Defer objects and using resolve() in each event handler, we can check when all the events have been triggered, regardless of sequence.

In this, clearly contrived, example, we want to check that SomeEmitter emits both event1 and event2, but we cannot guarantee the order.


const EventEmitter = require('events');
const Defer = require('easy-defer').default;

class SomeEmitter extends EventEmitter {

    start() {
        // guarantee asynchronous
        process.nextTick(this.send.bind(this));
    }

    send() {
        const args = [];
        if (Math.random() < 0.5) {
            this.emit('event1', args);
            this.emit('event2', args);
        } else {
            this.emit('event2', args);
            this.emit('event1', args);
        }
    }
}

describe('SomeEmitter', () => {

    it('emits multiple events', (done) => {
        const d1 = new Defer();
        const d2 = new Defer();

        const em = new SomeEmitter()
            .on('event1', (args) => {
                // expectations on args

                d1.resolve();
            })
            .on('event2', (args) => {
                // expectations on args

                d2.resolve();
            });

        Promise.all([d1, d2])
            .then(() => done());

        em.start();
    });
});

Separate Arrange from Act

Imagine the case where we have a download manager that implements some sort of throttling. We need to see whether it really throttles requests. There are a number of ways to approach this, but one way would be to use some Defer objects.

Here, the use of Defer allows us to configure all the requests without worrying that one of them will complete before we are ready.

    it('throttles downloads', async (done) => {
        const d1 = new Defer();
        const d2 = new Defer();

        nock('https://test.com')
            .get('/test1')
            .reply(200, async (_uri, _requestBody) => {
                await d1;           // this response will wait until we are ready
                return RESPONSE;
            });

        nock('https://test.com')
            .get('/test2')
            .reply(200, async (_uri, _requestBody) => {
                await d2;           // this response will wait until we are ready
                return RESPONSE;
            });

        nock('https://test.com')
            .get('/test3')
            .reply(200, (_uri, _requestBody) => {
                return RESPONSE;    // this response will not wait
            });

        const dm = new DownloadManager();
        const p1 = dm.downloadUrl('https://test.com/test1');
        const p2 = dm.downloadUrl('https://test.com/test2');
        const p3 = dm.downloadUrl('https://test.com/test3')
            .then(() => {
                // expect to be called after d2 has completed, but d1 is still waiting
                expect(d1.isPending).toBeTrue();
                expect(d2.isFulfilled).toBeTrue();
                 // resolve the last of them so all the downloads complete
                d1.resolve();
            });

        // Once all the promises are complete, we'll end the test
        Promise.all([p1, p2, p3])
            .then(() => done());

        // Guard conditions - everything is waiting
        expect(d1.isPending).toBeTrue();
        expect(d2.isPending).toBeTrue();

        // Completing one request should allow the third request to be serviced
        d2.resolve();
    });

In this test, if the download for p3 is not being throttled, it will start and finish immediately on declaration. The state of d2 will not match what is expected.