Created
May 27, 2016 15:28
-
-
Save molily/e200c72c13f63795c8e903d075dceb97 to your computer and use it in GitHub Desktop.
Cancelable Promise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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