ES2015 Deferred
This library contains a small constructor which produces deferred objects. These
are useful for testing purposes, and their use in production JS is discouraged
(it's usually better to use the promise constructor directly).
This module has no production dependencies.
installation
npm i es2015-deferred
ES2015
ES2015 module loaders can directly consume index.js
. If you're using
rollup or another ES2015 compatible module loader
configured to look for the module
field and in a
package.json
file, then you can import with:
import Deferred from 'es2015-deferred';
The
@rollup/plugin-node-resolve
package may be useful to you.
If you want to try this module out directly without installation, it can be used
via unpkg:
import Deferred from 'https://unpkg.com/es2015-deferred?module';
API
With Deferred
in your environment you can create deferred
objects.
var deferred = new Deferred();
var deferred = Deferred();
A promise is managed by the deferred object.
var promise = deferred.promise;
Resolve deferred.promise
with a given value.
deferred.resolve('a-resolution');
Reject deferred.promise
with an error.
deferred.reject(new Error('Oh noes!'));
Resolve and reject return the promise for easy chaining, e.g.
deferred.resolve('a-resolution').then();
example
This module is intended for use with unit test runners like
mocha. Let's say you want to test a module which makes a
request using
fetch
and parses the response as JSON:
export default function fetchAndParse(url) {
return self.fetch(url).then(response => {
if (!response.ok) {
throw new Error(`Unexpected response: ${response.status}`);
}
return response.json();
});
}
Fetch uses promises, so testing it can be tricky! Here's a set of tests for this
module written in mocha and sinon. What follows is bulky, but exhaustive. It can
be surprising how much branching promises hide!
import assert from 'assert';
import sinon from 'sinon';
import fetchAndParse from './fetchAndParse';
function failResolve() {
throw new Error('Promise should reject.');
}
describe('fetchAndParse', () => {
const sandbox = sinon.sandbox.create();
let fetchDeferred;
let promise;
beforeEach(() => {
fetchDeferred = new Deferred();
sandbox.stub(self, 'fetch').returns(fetchDeferred.promise);
promise = fetchAndParse('some-url');
});
afterEach(() => {
sandbox.reset();
});
it('calls fetch with the given url', () => {
assert.equal(self.fetch.callCount, 1);
assert.ok(self.fetch.calledWithExactly('some-url'));
});
describe('when the call to fetch rejects', () => {
let error;
beforeEach(() => {
error = new Error();
fetchDeferred.reject(error);
});
it('rejects the promise returned from fetchAndParse with the same error', () => {
return promise.then(
failResolve,
err => assert.equal(err, error)
);
});
});
describe('when the call to fetch resolves with an unsuccessful status', () => {
beforeEach(() => {
fetchDeferred.resolve({
ok: false,
status: 404
});
});
it('rejects with an unexpected response error', () => {
return promise.then(failResolve, err => {
assert.ok(err instanceof Error);
assert.equal(err.message, 'Unexpected response: 404');
});
});
});
describe('when the call to fetch resolves with a successful status', () => {
let jsonDeferred;
let response;
beforeEach(() => {
jsonDeferred = new Deferred();
response = {
ok: true,
json: sansbox.stub().returns(jsonDeferred.promise);
};
fetchDeferred.resolve(response);
});
it('calls the json method of the response object', () => {
return fetchDeferred.promise.then(() => assert.equal(response.json.callCount, 1));
});
describe('when response.json rejects', () => {
let error;
beforeEach(() => {
error = new Error();
jsonDeferred.reject(error);
});
it('rejects the promise returned from fetchAndParse with the same error', () => {
return promise.then(
failResolve,
err => assert.equal(err, error)
);
});
});
describe('when response.json resolves', () => {
beforeEach(() => {
jsonDeferred.resolve('parsed-response-body')
});
it('resolves the promise returned from fetchAndParse with the result', () => {
return promise.then(result => assert.equal(result, 'parsed-response-body'));
});
});
});
});