synchronous-promise
Advanced tools
Comparing version 1.0.12 to 1.0.13
@@ -9,23 +9,39 @@ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
function looksLikePromise(thing) { | ||
return thing && | ||
thing.then && | ||
typeof (thing.then) === "function" && | ||
typeof (thing.catch) === "function"; | ||
} | ||
function SynchronousPromise(ctorFunction) { | ||
this.status = "pending"; | ||
this._paused = false; | ||
this._next = []; | ||
this._catchFunctions = []; | ||
this._paused = false; | ||
this._data = []; | ||
this._runConstructorFunction(ctorFunction); | ||
} | ||
SynchronousPromise.prototype = { | ||
then: function (next, fail) { | ||
this._next.push([next, fail]); | ||
if (this._isPendingResolutionOrRejection()) { | ||
return this; | ||
} | ||
if (this.status === "resolved") { | ||
this._applyNext(); | ||
return this._applyNext(); | ||
} | ||
return this; | ||
return this._applyCatch(); | ||
}, | ||
catch: function (fn) { | ||
this._catchFunctions.push(fn); | ||
if (this.status === "rejected") { | ||
this._applyCatch(); | ||
this._next.push([undefined, fn]); | ||
if (this._isPendingResolutionOrRejection()) { | ||
return this; | ||
} | ||
return this; | ||
return this._applyCatch(); | ||
}, | ||
@@ -38,34 +54,31 @@ pause: function () { | ||
this._paused = false; | ||
var self = this; | ||
this._applyCatch().then(function () { | ||
self._applyNext(); | ||
}); | ||
return this; | ||
return this._applyNext(); | ||
}, | ||
_runConstructorFunction: function (ctorFunction) { | ||
var self = this; | ||
var doCatch = function (args) { | ||
self._catchData = argumentsToArray(args); | ||
this._next.push([ | ||
function (r) { return r; }, | ||
function (err) { throw err; } | ||
]); | ||
var isRun = false; | ||
ctorFunction(function (result) { | ||
if (isRun) { | ||
return; | ||
} | ||
isRun = true; | ||
self._setResolved(); | ||
self._data = [result]; | ||
self._applyNext(); | ||
}, function (err) { | ||
if (isRun) { | ||
return; | ||
} | ||
isRun = true; | ||
self._setRejected(); | ||
self._data = [err]; | ||
self._applyCatch(); | ||
}; | ||
ctorFunction(function () { | ||
self._data = argumentsToArray(arguments); | ||
var doResolve = function () { | ||
self._setResolved(); | ||
self._applyNext(); | ||
}; | ||
if (self._looksLikePromise(self._data[0])) { | ||
self._data[0].then(function () { | ||
self._data = argumentsToArray(arguments); | ||
doResolve(); | ||
}).catch(function () { | ||
doCatch(arguments); | ||
}); | ||
} else { | ||
doResolve(); | ||
} | ||
}, function () { | ||
self._setRejected(); | ||
doCatch(arguments); | ||
}); | ||
@@ -79,79 +92,116 @@ }, | ||
}, | ||
_setPending: function () { | ||
this.status = "pending"; | ||
}, | ||
_applyNext: function () { | ||
if (this.status === "rejected") { | ||
return this._applyCatch(); | ||
} | ||
if (this._next.length === 0 || this._paused) { | ||
return; | ||
return this; | ||
} | ||
var next = this._next.shift(); | ||
var next = this._findFirstResolutionHandler(); | ||
if (!next) { | ||
return this; | ||
} | ||
try { | ||
if (!next) { | ||
return; | ||
var data = next.apply(null, this._data); | ||
this._setResolved(); | ||
if (looksLikePromise(data)) { | ||
this._handleNestedPromise(data); | ||
return this; | ||
} | ||
var data = next[0].apply(null, this._data), | ||
self = this; | ||
if (this._looksLikePromise(data)) { | ||
data.then(function () { | ||
self._data = Array.prototype.slice.apply(arguments); | ||
self._applyNext(); | ||
}).catch(function (e) { | ||
throw e; | ||
}); | ||
} else { | ||
this._data = [data]; | ||
this._applyNext(); | ||
} | ||
this._data = [data]; | ||
return this._applyNext(); | ||
} catch (e) { | ||
this._next = []; | ||
this._data = undefined; | ||
if (next[1]) { | ||
try { next[1](e); } catch (ignore) { } | ||
} else { | ||
this._catchData = [e]; | ||
this._setRejected(); | ||
this._applyCatch(); | ||
} | ||
this._setRejected(); | ||
this._data = [e]; | ||
return this._applyCatch(); | ||
} | ||
}, | ||
_looksLikePromise: function (thing) { | ||
return thing && | ||
thing.then && | ||
typeof (thing.then) === "function"; | ||
}, | ||
_applyCatch: function () { | ||
if (this._paused || this._catchFunctions.length === 0) { | ||
return SynchronousPromise.resolve(); | ||
if (this.status === "resolved") { | ||
return this._applyNext(); | ||
} | ||
var catchFunction = (this._catchFunctions || []).shift(), | ||
catchData = this._catchData; | ||
if (!(catchFunction && catchData)) { | ||
return SynchronousPromise.resolve(); // nyom | ||
if (this._next.length === 0 || this._paused) { | ||
return this; | ||
} | ||
var result = catchFunction.apply(null, catchData); | ||
if (this._looksLikePromise(result)) { | ||
var self = this; | ||
result.then(function () { | ||
self._data = Array.prototype.slice.apply(arguments); | ||
self._setResolved(); | ||
self._applyNext(); | ||
}).catch(function () { | ||
self._catchData = Array.prototype.slice.apply(arguments); | ||
self._applyCatch(); | ||
}); | ||
} else { | ||
var next = this._findFirstRejectionHandler(); | ||
if (!next) { | ||
return this; | ||
} | ||
try { | ||
var data = next.apply(null, this._data); | ||
if (looksLikePromise(data)) { | ||
this._handleNestedPromise(data); | ||
return this; | ||
} | ||
this._setResolved(); | ||
this._data = [data]; | ||
return this._applyNext(); | ||
} catch (e) { | ||
this._setRejected(); | ||
this._next = []; | ||
this._data = undefined; | ||
this._data = [e]; | ||
return this._applyCatch(); | ||
} | ||
return SynchronousPromise.reject(); | ||
}, | ||
_handleNestedPromise: function (promise) { | ||
this._setPending(); | ||
var self = this; | ||
promise.then(function (d) { | ||
self._setResolved(); | ||
self._data = [d]; | ||
self._applyNext(); | ||
}).catch(function (e) { | ||
self._setRejected(); | ||
self._data = [e]; | ||
self._applyCatch(); | ||
}); | ||
}, | ||
_isPendingResolutionOrRejection: function () { | ||
return this.status === "pending"; | ||
}, | ||
_findFirstResolutionHandler: function () { | ||
var next; | ||
while (!next && this._next.length > 0) { | ||
next = this._next.shift()[0]; | ||
} | ||
return next; | ||
}, | ||
_findFirstRejectionHandler: function () { | ||
var next; | ||
while (!next && this._next.length > 0) { | ||
next = this._next.shift()[1]; | ||
} | ||
return next; | ||
} | ||
}; | ||
SynchronousPromise.resolve = function () { | ||
var args = arguments; | ||
SynchronousPromise.resolve = function (data) { | ||
if (looksLikePromise(data)) { | ||
return data; | ||
} | ||
return new SynchronousPromise(function (resolve) { | ||
resolve.apply(null, argumentsToArray(args)); | ||
resolve(data); | ||
}); | ||
}; | ||
SynchronousPromise.reject = function () { | ||
var args = arguments; | ||
SynchronousPromise.reject = function (error) { | ||
if (looksLikePromise(error)) { | ||
return error; | ||
} | ||
return new SynchronousPromise(function (resolve, reject) { | ||
reject.apply(null, argumentsToArray(args)); | ||
reject(error); | ||
}); | ||
@@ -207,2 +257,3 @@ }; | ||
},{}]},{},[1]); |
@@ -0,0 +0,0 @@ export interface SynchronousPromise<T> extends Promise<T> { |
237
index.js
@@ -6,23 +6,39 @@ "use strict"; | ||
function looksLikePromise(thing) { | ||
return thing && | ||
thing.then && | ||
typeof (thing.then) === "function" && | ||
typeof (thing.catch) === "function"; | ||
} | ||
function SynchronousPromise(ctorFunction) { | ||
this.status = "pending"; | ||
this._paused = false; | ||
this._next = []; | ||
this._catchFunctions = []; | ||
this._paused = false; | ||
this._data = []; | ||
this._runConstructorFunction(ctorFunction); | ||
} | ||
SynchronousPromise.prototype = { | ||
then: function (next, fail) { | ||
this._next.push([next, fail]); | ||
if (this._isPendingResolutionOrRejection()) { | ||
return this; | ||
} | ||
if (this.status === "resolved") { | ||
this._applyNext(); | ||
return this._applyNext(); | ||
} | ||
return this; | ||
return this._applyCatch(); | ||
}, | ||
catch: function (fn) { | ||
this._catchFunctions.push(fn); | ||
if (this.status === "rejected") { | ||
this._applyCatch(); | ||
this._next.push([undefined, fn]); | ||
if (this._isPendingResolutionOrRejection()) { | ||
return this; | ||
} | ||
return this; | ||
return this._applyCatch(); | ||
}, | ||
@@ -35,34 +51,31 @@ pause: function () { | ||
this._paused = false; | ||
var self = this; | ||
this._applyCatch().then(function () { | ||
self._applyNext(); | ||
}); | ||
return this; | ||
return this._applyNext(); | ||
}, | ||
_runConstructorFunction: function (ctorFunction) { | ||
var self = this; | ||
var doCatch = function (args) { | ||
self._catchData = argumentsToArray(args); | ||
this._next.push([ | ||
function (r) { return r; }, | ||
function (err) { throw err; } | ||
]); | ||
var isRun = false; | ||
ctorFunction(function (result) { | ||
if (isRun) { | ||
return; | ||
} | ||
isRun = true; | ||
self._setResolved(); | ||
self._data = [result]; | ||
self._applyNext(); | ||
}, function (err) { | ||
if (isRun) { | ||
return; | ||
} | ||
isRun = true; | ||
self._setRejected(); | ||
self._data = [err]; | ||
self._applyCatch(); | ||
}; | ||
ctorFunction(function () { | ||
self._data = argumentsToArray(arguments); | ||
var doResolve = function () { | ||
self._setResolved(); | ||
self._applyNext(); | ||
}; | ||
if (self._looksLikePromise(self._data[0])) { | ||
self._data[0].then(function () { | ||
self._data = argumentsToArray(arguments); | ||
doResolve(); | ||
}).catch(function () { | ||
doCatch(arguments); | ||
}); | ||
} else { | ||
doResolve(); | ||
} | ||
}, function () { | ||
self._setRejected(); | ||
doCatch(arguments); | ||
}); | ||
@@ -76,79 +89,116 @@ }, | ||
}, | ||
_setPending: function () { | ||
this.status = "pending"; | ||
}, | ||
_applyNext: function () { | ||
if (this.status === "rejected") { | ||
return this._applyCatch(); | ||
} | ||
if (this._next.length === 0 || this._paused) { | ||
return; | ||
return this; | ||
} | ||
var next = this._next.shift(); | ||
var next = this._findFirstResolutionHandler(); | ||
if (!next) { | ||
return this; | ||
} | ||
try { | ||
if (!next) { | ||
return; | ||
var data = next.apply(null, this._data); | ||
this._setResolved(); | ||
if (looksLikePromise(data)) { | ||
this._handleNestedPromise(data); | ||
return this; | ||
} | ||
var data = next[0].apply(null, this._data), | ||
self = this; | ||
if (this._looksLikePromise(data)) { | ||
data.then(function () { | ||
self._data = Array.prototype.slice.apply(arguments); | ||
self._applyNext(); | ||
}).catch(function (e) { | ||
throw e; | ||
}); | ||
} else { | ||
this._data = [data]; | ||
this._applyNext(); | ||
} | ||
this._data = [data]; | ||
return this._applyNext(); | ||
} catch (e) { | ||
this._next = []; | ||
this._data = undefined; | ||
if (next[1]) { | ||
try { next[1](e); } catch (ignore) { } | ||
} else { | ||
this._catchData = [e]; | ||
this._setRejected(); | ||
this._applyCatch(); | ||
} | ||
this._setRejected(); | ||
this._data = [e]; | ||
return this._applyCatch(); | ||
} | ||
}, | ||
_looksLikePromise: function (thing) { | ||
return thing && | ||
thing.then && | ||
typeof (thing.then) === "function"; | ||
}, | ||
_applyCatch: function () { | ||
if (this._paused || this._catchFunctions.length === 0) { | ||
return SynchronousPromise.resolve(); | ||
if (this.status === "resolved") { | ||
return this._applyNext(); | ||
} | ||
var catchFunction = (this._catchFunctions || []).shift(), | ||
catchData = this._catchData; | ||
if (!(catchFunction && catchData)) { | ||
return SynchronousPromise.resolve(); // nyom | ||
if (this._next.length === 0 || this._paused) { | ||
return this; | ||
} | ||
var result = catchFunction.apply(null, catchData); | ||
if (this._looksLikePromise(result)) { | ||
var self = this; | ||
result.then(function () { | ||
self._data = Array.prototype.slice.apply(arguments); | ||
self._setResolved(); | ||
self._applyNext(); | ||
}).catch(function () { | ||
self._catchData = Array.prototype.slice.apply(arguments); | ||
self._applyCatch(); | ||
}); | ||
} else { | ||
var next = this._findFirstRejectionHandler(); | ||
if (!next) { | ||
return this; | ||
} | ||
try { | ||
var data = next.apply(null, this._data); | ||
if (looksLikePromise(data)) { | ||
this._handleNestedPromise(data); | ||
return this; | ||
} | ||
this._setResolved(); | ||
this._data = [data]; | ||
return this._applyNext(); | ||
} catch (e) { | ||
this._setRejected(); | ||
this._next = []; | ||
this._data = undefined; | ||
this._data = [e]; | ||
return this._applyCatch(); | ||
} | ||
return SynchronousPromise.reject(); | ||
}, | ||
_handleNestedPromise: function (promise) { | ||
this._setPending(); | ||
var self = this; | ||
promise.then(function (d) { | ||
self._setResolved(); | ||
self._data = [d]; | ||
self._applyNext(); | ||
}).catch(function (e) { | ||
self._setRejected(); | ||
self._data = [e]; | ||
self._applyCatch(); | ||
}); | ||
}, | ||
_isPendingResolutionOrRejection: function () { | ||
return this.status === "pending"; | ||
}, | ||
_findFirstResolutionHandler: function () { | ||
var next; | ||
while (!next && this._next.length > 0) { | ||
next = this._next.shift()[0]; | ||
} | ||
return next; | ||
}, | ||
_findFirstRejectionHandler: function () { | ||
var next; | ||
while (!next && this._next.length > 0) { | ||
next = this._next.shift()[1]; | ||
} | ||
return next; | ||
} | ||
}; | ||
SynchronousPromise.resolve = function () { | ||
var args = arguments; | ||
SynchronousPromise.resolve = function (data) { | ||
if (looksLikePromise(data)) { | ||
return data; | ||
} | ||
return new SynchronousPromise(function (resolve) { | ||
resolve.apply(null, argumentsToArray(args)); | ||
resolve(data); | ||
}); | ||
}; | ||
SynchronousPromise.reject = function () { | ||
var args = arguments; | ||
SynchronousPromise.reject = function (error) { | ||
if (looksLikePromise(error)) { | ||
return error; | ||
} | ||
return new SynchronousPromise(function (resolve, reject) { | ||
reject.apply(null, argumentsToArray(args)); | ||
reject(error); | ||
}); | ||
@@ -203,1 +253,2 @@ }; | ||
}; | ||
@@ -32,13 +32,24 @@ /* | ||
describe("then", function () { | ||
it("should return the same promise", function () { | ||
it("should return the same resolved promise", function () { | ||
var sut = createResolved(); | ||
expect(sut.then(function () { })).to.equal(sut); | ||
expect(sut.then(null, function () { })).to.equal(sut); | ||
}); | ||
it("should return the same promise v2", function () { | ||
it("should return the same resolved promise v2", function () { | ||
var | ||
result = createResolved().then(function () { | ||
/* purposely don"t return anything */ | ||
/* purposely don't return anything */ | ||
}); | ||
expect(result).to.be.instanceOf(SynchronousPromise); | ||
}); | ||
it("should return the same rejected promise", function () { | ||
var sut = createRejected(); | ||
expect(sut.then(function () { })).to.equal(sut); | ||
}); | ||
it("should return the same rejected promise v2", function () { | ||
var result = createRejected().then(function () { | ||
/* purposely don't return anything */ | ||
}); | ||
expect(result).to.be.instanceOf(SynchronousPromise) | ||
}); | ||
it("should call into the catch function when the function given to then throws", function () { | ||
@@ -59,3 +70,3 @@ var | ||
}); | ||
it("should call into the failure function when the predecessor fails", function () { | ||
it("should call into following catch rather than the sibling onRejected if onResolved fails", function () { | ||
var | ||
@@ -74,4 +85,4 @@ sut = createResolved(), | ||
expect(captured).to.equal(expected); | ||
expect(catchCaptured).to.be.null; | ||
expect(captured).to.be.null; | ||
expect(catchCaptured).to.equal(expected); | ||
}); | ||
@@ -87,2 +98,12 @@ it("should bring the first resolve value into the first then", function () { | ||
}); | ||
it("should bring the first rejected value into the first onRejected then handler", function () { | ||
var | ||
initial = new Error("123"), | ||
captured = null; | ||
createRejected(initial).then(function () { | ||
}, function (e) { | ||
captured = e | ||
}); | ||
expect(captured).to.equal(initial); | ||
}); | ||
it("should resolve when the first resolution is a resolved promise", function () { | ||
@@ -96,3 +117,3 @@ var | ||
expect(captured).to.equal("123"); | ||
}) | ||
}); | ||
it("should catch when the first resolution is a rejected promise", function () { | ||
@@ -207,2 +228,15 @@ var | ||
}); | ||
it("should call handler if the promise resolves", function () { | ||
// Arrange | ||
var | ||
sut = SynchronousPromise.unresolved(), | ||
resolved = false, | ||
caught = false; | ||
// Act | ||
sut.then(() => resolved = true).catch(() => caught = true); | ||
sut.resolve(); | ||
// Assert | ||
expect(resolved).to.be.true; | ||
expect(caught).to.be.false; | ||
}); | ||
it("should be called on a delayed rejection", function () { | ||
@@ -224,3 +258,3 @@ var | ||
}); | ||
it("should return the same promise", function () { | ||
it("should return a resolved promise if doesn't thrown an error", function () { | ||
var | ||
@@ -234,3 +268,3 @@ promise = createRejected("123"), | ||
expect(result).to.be.instanceOf(SynchronousPromise); | ||
expect(result).to.equal(promise); | ||
expect(result.status).to.be.equal("resolved"); | ||
}); | ||
@@ -251,4 +285,19 @@ it("should not interfere with a later then if there is no error", function () { | ||
}); | ||
it("should prevent then handlers after the error from being called", function () { | ||
it("should not be called if the promise is handled successful by a previous onRejected handler", function () { | ||
var | ||
expected = new Error("123"), | ||
notExpected = new Error("Not expected"), | ||
capturedError = null; | ||
createRejected(expected).then(function () {}, function (e) { | ||
capturedError = e | ||
}) | ||
.catch(function () { | ||
/* purposely don't return anything */ | ||
capturedError = notExpected; | ||
}) | ||
expect(capturedError).to.equal(expected); | ||
}); | ||
it("should prevent the handlers after the error from being called", function () { | ||
var | ||
captured = null; | ||
@@ -286,2 +335,35 @@ createResolved("123").catch(function (e) { | ||
setTimeout(function () { | ||
try { | ||
expect(capturedA).to.equal(expected); | ||
expect(capturedB).to.equal(expected); | ||
done(); | ||
} catch (e) { | ||
done(e); | ||
} | ||
}, 100); | ||
}); | ||
it("should re-catch if a then onRejected handler returns a rejected promise", function (done) { | ||
// Arrange | ||
var | ||
expected = "123", | ||
pausedRejectedPromise = SynchronousPromise.reject(expected).pause(), | ||
capturedA = null, | ||
capturedB = null; | ||
pausedRejectedPromise.then(function () { | ||
/* purposely don't return anything */ | ||
}, function (e) { | ||
capturedA = e; | ||
// prove that this works even from an async promise | ||
return Promise.reject(e); | ||
}).catch(function (e) { | ||
capturedB = e; | ||
}) | ||
// Act | ||
pausedRejectedPromise.resume(); | ||
// Assert | ||
setTimeout(function () { | ||
expect(capturedA).to.equal(expected); | ||
@@ -317,6 +399,10 @@ expect(capturedB).to.equal(expected); | ||
setTimeout(function () { | ||
expect(capturedA).to.equal(expected); | ||
expect(capturedB).to.be.null; | ||
expect(secondResolve).to.equal("456"); | ||
done(); | ||
try { | ||
expect(capturedA).to.equal(expected); | ||
expect(capturedB).to.be.null; | ||
expect(secondResolve).to.equal("456"); | ||
done(); | ||
} catch (e) { | ||
done(e); | ||
} | ||
}, 100); | ||
@@ -370,3 +456,3 @@ }); | ||
describe("starting paused", function () { | ||
it("should return a promise in paused state with no initial data", function () { | ||
it("should return a promise in paused state with no initial data and being resolved on resume", function () { | ||
var | ||
@@ -383,2 +469,30 @@ captured, | ||
}); | ||
it("should return a promise in paused state with no initial data and being rejected on resume", function () { | ||
var | ||
captured, | ||
expected = new Error("moon"), | ||
promise = SynchronousPromise.resolve().pause().then(function () { | ||
throw expected | ||
}).catch(function (e) { | ||
captured = e; | ||
}); | ||
expect(captured).not.to.be.defined; | ||
promise.resume(); | ||
expect(captured).to.equal(expected); | ||
}); | ||
it("should return a promise in paused state with no initial data and being resolved after a catch on resume", function () { | ||
var | ||
captured, | ||
error = new Error("moon"), | ||
promise = SynchronousPromise.resolve().pause().then(function () { | ||
throw error | ||
}).catch(function (e) { | ||
return e.message; | ||
}).then(function (m) { | ||
captured = m; | ||
}); | ||
expect(captured).not.to.be.defined; | ||
promise.resume(); | ||
expect(captured).to.equal("moon"); | ||
}); | ||
}); | ||
@@ -645,2 +759,2 @@ }); | ||
}); | ||
}) | ||
}); |
{ | ||
"name": "synchronous-promise", | ||
"version": "1.0.12", | ||
"version": "1.0.13", | ||
"description": "Synchronous Promise-like prototype to use in testing where you would have used an ES6 Promise", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49463
1231