Skip to content

Instantly share code, notes, and snippets.

@molily
Created May 27, 2016 15:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save molily/e200c72c13f63795c8e903d075dceb97 to your computer and use it in GitHub Desktop.
Save molily/e200c72c13f63795c8e903d075dceb97 to your computer and use it in GitHub Desktop.
Cancelable Promise
import cancelablePromise, { CancelError } from '../cancelablePromise';
const originalError = new Error('original error');
const expectCancelError = (reason, message) => {
expect(reason).toEqual(jasmine.any(CancelError));
expect(reason.name).toBe('CancelError');
expect(reason.message).toBe(message || 'Promise was cancelled');
};
describe('cancelablePromise', () => {
it('passes through the original fulfilled promise', (done) => {
const promise = new Promise((resolve) => {
resolve(123);
});
const newPromise = cancelablePromise(promise);
newPromise.then(done, done.fail);
});
it('passes through the original rejected promise', (done) => {
const promise = new Promise((resolve, reject) => {
reject(originalError);
});
const newPromise = cancelablePromise(promise);
const onError = (reason) => {
expect(reason).toBe(originalError);
};
newPromise.then(fail, onError).then(done, done.fail);
});
it('cancels the original fulfilled promise', (done) => {
const promise = new Promise((resolve) => {
resolve(123);
});
const newPromise = cancelablePromise(promise);
newPromise.cancel();
const onError = (reason) => {
expectCancelError(reason);
};
newPromise.then(fail, onError).then(done, done.fail);
});
it('cancels the original rejected promise', (done) => {
const promise = new Promise((resolve, reject) => {
reject(originalError);
});
const newPromise = cancelablePromise(promise);
newPromise.cancel();
const onError = (reason) => {
expectCancelError(reason);
};
newPromise.then(fail, onError).then(done, done.fail);
});
it('cancels with a custom error message', (done) => {
const customErrorMessage = 'does not compute';
const promise = new Promise((resolve) => {
resolve(123);
});
const newPromise = cancelablePromise(promise);
newPromise.cancel(customErrorMessage);
const onError = (reason) => {
expectCancelError(reason, customErrorMessage);
};
newPromise.then(fail, onError).then(done, done.fail);
});
it('cancels a promise that is resolved later', (done) => {
let resolve;
const promise = new Promise((_resolve) => {
resolve = _resolve;
});
const newPromise = cancelablePromise(promise);
newPromise.cancel();
resolve(123);
const onError = (reason) => {
expectCancelError(reason);
};
newPromise.then(fail, onError).then(done, done.fail);
});
});
// Manual inheritance without super call. We can’t use `class` declaration
// since the Babel compilation output will call Error.call(this, arguments),
// which always returns a new Error instance instead of the instance.
// See http://www.ecma-international.org/ecma-262/6.0/#sec-error-constructor
// Apart from this change, we’re using Babel’s approach of inheritance:
// https://github.com/babel/babel/blob/v6.9.0/packages/babel-helpers/src/helpers.js#L210-L225
/* eslint-disable func-style */
export function CancelError(message) {
this.message = message;
}
/* eslint-enable func-style */
CancelError.prototype = Object.create(Error.prototype, {
constructor: {
value: CancelError,
enumerable: false,
writable: true,
configurable: true
},
name: {
value: 'CancelError',
enumerable: true,
writable: false,
configurable: false
}
});
// Inherit static methods
if (Object.setPrototypeOf) {
Object.setPrototypeOf(CancelError, Error);
} else {
/* eslint-disable no-proto */
CancelError.__proto__ = Error;
/* eslint-enable no-proto */
}
// Based on the given promise, returns a new promise that is cancelable.
// The cancelable promise has an additional `cancel` method.
export default (promise) => {
let cancelled = false;
let cancelMessage = 'Promise was cancelled';
const onSuccess = (result) => {
if (cancelled) {
throw new CancelError(cancelMessage);
}
return result;
};
const onError = (reason) => {
if (cancelled) {
throw new CancelError(cancelMessage);
}
throw reason;
};
const cancelablePromise = promise.then(onSuccess, onError);
cancelablePromise.cancel = (message) => {
cancelled = true;
if (message) {
cancelMessage = message;
}
return cancelablePromise;
};
return cancelablePromise;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment