ts-promise
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -1,2 +0,2 @@ | ||
export { default, Promise, Thenable, UnhandledRejectionError, Deferred } from "./Promise"; | ||
export { default, Promise, Thenable, UnhandledRejectionError, Deferred, VoidDeferred } from "./Promise"; | ||
export { default as BaseError } from "./BaseError"; |
@@ -0,1 +1,2 @@ | ||
import Trace from "./Trace"; | ||
import BaseError from "./BaseError"; | ||
@@ -7,3 +8,4 @@ export interface Thenable<T> { | ||
reason: any; | ||
constructor(reason: any); | ||
trace: Trace; | ||
constructor(reason: any, trace: Trace); | ||
} | ||
@@ -56,3 +58,3 @@ /** | ||
*/ | ||
resolve: (value?: void) => void; | ||
resolve: (value?: void | Thenable<void>) => void; | ||
} | ||
@@ -75,2 +77,4 @@ export declare class Promise<T> implements Thenable<T> { | ||
reason(): any; | ||
inspect(): string; | ||
toString(): string; | ||
static resolve<R>(value: R | Thenable<R>): Promise<R>; | ||
@@ -92,3 +96,3 @@ static resolve(): Promise<void>; | ||
private _followThenable(slave, then); | ||
private _enqueue(slave, onFulfilled, onRejected); | ||
private _enqueue(onFulfilled, onRejected, slave, done); | ||
private _flush(); | ||
@@ -95,0 +99,0 @@ private _unwrap(handler); |
@@ -27,5 +27,6 @@ /** | ||
__extends(UnhandledRejectionError, _super); | ||
function UnhandledRejectionError(reason) { | ||
function UnhandledRejectionError(reason, trace) { | ||
_super.call(this, "UnhandledRejectionError", "unhandled rejection: " + reason); | ||
this.reason = reason; | ||
this.trace = trace; | ||
} | ||
@@ -50,2 +51,3 @@ return UnhandledRejectionError; | ||
} | ||
var dummyDoneTrace = new Trace_1.default(); | ||
/** | ||
@@ -133,11 +135,18 @@ * Currently unwrapping promise, while running one of its then-callbacks. | ||
slave._setSource(this); | ||
this._enqueue(slave, onFulfilled, onRejected); | ||
this._enqueue(onFulfilled, onRejected, slave, undefined); | ||
return slave; | ||
}; | ||
Promise.prototype.done = function (onFulfilled, onRejected) { | ||
trace && trace(this, "done(" + typeof onFulfilled + ", " + typeof onRejected + ")"); | ||
if (this._state === 1 /* Fulfilled */ && typeof onFulfilled !== "function") { | ||
return; | ||
} | ||
trace && trace(this, "done(" + typeof onFulfilled + ", " + typeof onRejected + ")"); | ||
this._enqueue(undefined, onFulfilled, onRejected); | ||
var doneTrace = dummyDoneTrace; | ||
if (longTraces) { | ||
doneTrace = new Trace_1.default(); | ||
if (this._trace) { | ||
doneTrace.setSource(this._trace); | ||
} | ||
} | ||
this._enqueue(onFulfilled, onRejected, undefined, doneTrace); | ||
}; | ||
@@ -168,2 +177,20 @@ Promise.prototype.catch = function (onRejected) { | ||
}; | ||
Promise.prototype.inspect = function () { | ||
return this.toString(); | ||
}; | ||
Promise.prototype.toString = function () { | ||
var state; | ||
switch (this._state) { | ||
case 0 /* Pending */: | ||
state = "pending"; | ||
break; | ||
case 1 /* Fulfilled */: | ||
state = "fulfilled"; | ||
break; | ||
case 2 /* Rejected */: | ||
state = "rejected"; | ||
break; | ||
} | ||
return "[Promise " + this._id + ": " + state + "]"; | ||
}; | ||
Promise.resolve = function (value) { | ||
@@ -336,3 +363,3 @@ var p = new Promise(internalResolver); | ||
trace && trace(this, "_follow([Promise " + slave._id + "])"); | ||
slave._enqueue(this, undefined, undefined); | ||
slave._enqueue(undefined, undefined, this, undefined); | ||
}; | ||
@@ -383,4 +410,10 @@ Promise.prototype._followThenable = function (slave, then) { | ||
}; | ||
Promise.prototype._enqueue = function (slave, onFulfilled, onRejected) { | ||
var h = { promise: this, onFulfilled: onFulfilled, onRejected: onRejected, slave: slave }; | ||
Promise.prototype._enqueue = function (onFulfilled, onRejected, slave, done) { | ||
var h = { | ||
promise: this, | ||
onFulfilled: onFulfilled, | ||
onRejected: onRejected, | ||
slave: slave, | ||
done: done | ||
}; | ||
if (this._state !== 0 /* Pending */) { | ||
@@ -410,3 +443,7 @@ async_1.default.enqueue(Promise._unwrapper, h); | ||
i++; | ||
if (handler.slave) { | ||
if (handler.done) { | ||
// .done(...) callbacks | ||
async_1.default.enqueue(Promise._unwrapper, handler); | ||
} | ||
else { | ||
if (!handler.onFulfilled && !handler.onRejected) { | ||
@@ -423,10 +460,6 @@ // we're the return value of an onFulfilled, tell our | ||
else { | ||
// .then() callbacks, including the returned promise from .then() | ||
// .then(...) callbacks, including the returned promise from .then() | ||
async_1.default.enqueue(Promise._unwrapper, handler); | ||
} | ||
} | ||
else { | ||
// .done() callbacks | ||
async_1.default.enqueue(Promise._unwrapper, handler); | ||
} | ||
} | ||
@@ -436,4 +469,3 @@ }; | ||
var callback = this._state === 1 /* Fulfilled */ ? handler.onFulfilled : handler.onRejected; | ||
var slave = handler.slave; | ||
if (!slave) { | ||
if (handler.done) { | ||
// Unwrap .done() callbacks | ||
@@ -445,3 +477,4 @@ trace && trace(this, "_unwrap()"); | ||
if (this._state === 2 /* Rejected */) { | ||
throw new UnhandledRejectionError(this._result); | ||
var unhandled = new UnhandledRejectionError(this._result, handler.done); | ||
throw unhandled; // TODO Allow intercepting these | ||
} | ||
@@ -452,15 +485,16 @@ return; | ||
unwrappingPromise = this; | ||
// Don't try-catch, in order to let it break immediately | ||
try { | ||
var result = callback(this._result); | ||
if (!result) { | ||
// Common case: no result value | ||
return; | ||
if (result) { | ||
// May be a thenable, need to start following it... | ||
var p = (result instanceof Promise) ? result : Promise.resolve(result); | ||
p.done(); // Ensure it throws as soon as it's rejected | ||
} | ||
// May be a thenable, need to start following it... | ||
var p = (result instanceof Promise) ? result : Promise.resolve(result); | ||
p.done(); // Ensure it throws as soon as it's rejected | ||
unwrappingPromise = undefined; | ||
} | ||
finally { | ||
catch (e) { | ||
unwrappingPromise = undefined; | ||
// Wrap in UnhandledRejectionError | ||
var unhandled = new UnhandledRejectionError(e, handler.done); | ||
throw unhandled; // TODO Allow intercepting these | ||
} | ||
@@ -470,2 +504,3 @@ return; | ||
// Unwrap .then() calbacks | ||
var slave = handler.slave; | ||
trace && trace(this, "_unwrap(" + slave._id + ")"); | ||
@@ -472,0 +507,0 @@ if (typeof callback === "function") { |
@@ -13,2 +13,3 @@ /** | ||
var chai = require("chai"); | ||
var Trace_1 = require("../lib/Trace"); | ||
var Promise_1 = require("../lib/Promise"); | ||
@@ -189,2 +190,8 @@ var expect = chai.expect; | ||
}); | ||
it("VoidDeferred can be resolved using Thenable", function () { | ||
var d = Promise_1.Promise.defer(); | ||
d.resolve(Promise_1.Promise.resolve()); // Mostly for the TS typing | ||
Promise_1.Promise.flush(); | ||
expect(d.promise.isFulfilled()).to.equal(true); | ||
}); | ||
}); | ||
@@ -205,3 +212,3 @@ describe("#then()", function () { | ||
Promise_1.Promise.resolve(42).done(); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
@@ -212,19 +219,19 @@ it("is silent on later resolved promise", function () { | ||
d.resolve(42); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
it("is silent when its fulfill callback returns non-Error", function () { | ||
Promise_1.Promise.resolve(42).done(function (v) { return undefined; }); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
it("is silent when its fulfill callback returns non-Error Promise", function () { | ||
Promise_1.Promise.resolve(42).done(function (v) { return Promise_1.Promise.resolve(); }); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
it("is silent when its reject callback returns non-Error", function () { | ||
Promise_1.Promise.reject(new Error("boom")).done(null, function (r) { return undefined; }); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
it("is silent when its reject callback returns non-Error Promise", function () { | ||
Promise_1.Promise.reject(new Error("boom")).done(null, function (r) { return Promise_1.Promise.resolve(); }); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
@@ -236,11 +243,11 @@ it("is silent when its reject callback returns non-Error Thenable", function () { | ||
Promise_1.Promise.reject(new Error("boom")).done(null, function (r) { return thenable; }); | ||
expect(Promise_1.Promise.flush).to.not.throw(); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.not.throw(); | ||
}); | ||
it("should immediately break on thrown error", function () { | ||
it("should immediately break on thrown error in normal callback", function () { | ||
var ready = false; | ||
Promise_1.Promise.reject(new Error("boom")).done(); | ||
Promise_1.Promise.resolve().done(function () { throw new Error("boom"); }); | ||
Promise_1.Promise.resolve().then(function () { | ||
ready = true; | ||
}); | ||
expect(Promise_1.Promise.flush).to.throw(Promise_1.UnhandledRejectionError); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
expect(ready).to.be.false; | ||
@@ -250,11 +257,41 @@ Promise_1.Promise.flush(); | ||
}); | ||
it("should immediately break on returned rejection", function () { | ||
var p = Promise_1.Promise.resolve(); | ||
p.then(function () { | ||
it("should immediately break on thrown error in error callback", function () { | ||
var ready = false; | ||
Promise_1.Promise.reject(new Error("dummy")).done(undefined, function () { throw new Error("boom"); }); | ||
Promise_1.Promise.resolve().then(function () { | ||
ready = true; | ||
}); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
expect(ready).to.be.false; | ||
Promise_1.Promise.flush(); | ||
expect(ready).to.be.true; | ||
}); | ||
it("should immediately break on returned rejection in callback", function () { | ||
Promise_1.Promise.resolve().done(function () { | ||
return Promise_1.Promise.reject(new Error("boom")); | ||
}).done(); | ||
expect(Promise_1.Promise.flush).to.throw(Promise_1.UnhandledRejectionError); | ||
}); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
}); | ||
it("should immediately break on asynchronously rejected Thenable", function () { | ||
var d = Promise_1.Promise.defer(); | ||
Promise_1.Promise.resolve().done(function () { | ||
return d.promise; | ||
}); | ||
Promise_1.Promise.flush(); | ||
d.reject(new Error("boom")); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
}); | ||
it("should break on already rejected promise", function () { | ||
var ready = false; | ||
Promise_1.Promise.reject(new Error("boom")).done(); | ||
Promise_1.Promise.resolve().then(function () { | ||
ready = true; | ||
}); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
expect(ready).to.be.false; | ||
Promise_1.Promise.flush(); | ||
expect(ready).to.be.true; | ||
}); | ||
it("should break on asynchronously rejected Promise", function () { | ||
var d = Promise_1.Promise.defer(); | ||
var p = Promise_1.Promise.resolve(); | ||
@@ -266,4 +303,50 @@ p.then(function () { | ||
d.reject(new Error("boom")); | ||
expect(Promise_1.Promise.flush).to.throw(Promise_1.UnhandledRejectionError); | ||
expect(function () { return Promise_1.Promise.flush(); }).to.throw(Promise_1.UnhandledRejectionError); | ||
}); | ||
it("should support long traces on throw from callback", function () { | ||
Promise_1.Promise.setLongTraces(true); | ||
Promise_1.Promise.resolve().done(function () { throw new Error("boom"); }); | ||
var caught; | ||
try { | ||
Promise_1.Promise.flush(); | ||
} | ||
catch (e) { | ||
caught = e; | ||
} | ||
expect(caught).to.be.instanceof(Promise_1.UnhandledRejectionError); | ||
// TODO: assert the trace property for correctness | ||
expect(caught.trace.inspect()).to.not.contain("no trace"); | ||
Promise_1.Promise.setLongTraces(false); | ||
}); | ||
it("should support long traces on throw from callback without non-long-trace source", function () { | ||
var p = Promise_1.Promise.resolve(); | ||
Promise_1.Promise.setLongTraces(true); | ||
p.done(function () { throw new Error("boom"); }); | ||
var caught; | ||
try { | ||
Promise_1.Promise.flush(); | ||
} | ||
catch (e) { | ||
caught = e; | ||
} | ||
expect(caught).to.be.instanceof(Promise_1.UnhandledRejectionError); | ||
// TODO: assert the trace property for correctness | ||
expect(caught.trace.inspect()).to.not.contain("no trace"); | ||
Promise_1.Promise.setLongTraces(false); | ||
}); | ||
it("should support long traces on rejection without callbacks", function () { | ||
Promise_1.Promise.setLongTraces(true); | ||
Promise_1.Promise.reject(new Error("boom")).done(); | ||
var caught; | ||
try { | ||
Promise_1.Promise.flush(); | ||
} | ||
catch (e) { | ||
caught = e; | ||
} | ||
expect(caught).to.be.instanceof(Promise_1.UnhandledRejectionError); | ||
// TODO: assert the trace property for correctness | ||
expect(caught.trace.inspect()).to.not.contain("no trace"); | ||
Promise_1.Promise.setLongTraces(false); | ||
}); | ||
}); // #done() | ||
@@ -361,2 +444,30 @@ describe("#isFulfilled()", function () { | ||
}); | ||
describe("#toString()", function () { | ||
it("returns a readable representation for a pending Promise", function () { | ||
var p = Promise_1.Promise.defer().promise; | ||
expect(p.toString()).to.match(/^\[Promise \d+: pending\]$/); | ||
}); | ||
it("returns a readable representation for a fulfilled Promise", function () { | ||
var p = Promise_1.Promise.resolve(); | ||
expect(p.toString()).to.match(/^\[Promise \d+: fulfilled\]$/); | ||
}); | ||
it("returns a readable representation for a rejected Promise", function () { | ||
var p = Promise_1.Promise.reject(new Error("boom")); | ||
expect(p.toString()).to.match(/^\[Promise \d+: rejected\]$/); | ||
}); | ||
}); | ||
describe("#inspect()", function () { | ||
it("returns a readable representation for a pending Promise", function () { | ||
var p = Promise_1.Promise.defer().promise; | ||
expect(p.inspect()).to.match(/^\[Promise \d+: pending\]$/); | ||
}); | ||
it("returns a readable representation for a fulfilled Promise", function () { | ||
var p = Promise_1.Promise.resolve(); | ||
expect(p.inspect()).to.match(/^\[Promise \d+: fulfilled\]$/); | ||
}); | ||
it("returns a readable representation for a rejected Promise", function () { | ||
var p = Promise_1.Promise.reject(new Error("boom")); | ||
expect(p.inspect()).to.match(/^\[Promise \d+: rejected\]$/); | ||
}); | ||
}); | ||
describe("long stack traces", function () { | ||
@@ -444,3 +555,3 @@ before(function () { | ||
var e = new Error("boom"); | ||
var ure = new Promise_1.UnhandledRejectionError(e); | ||
var ure = new Promise_1.UnhandledRejectionError(e, new Trace_1.default()); | ||
expect(ure.message).to.contain("Error: boom"); | ||
@@ -450,3 +561,3 @@ }); | ||
var e = new Error("boom"); | ||
var ure = new Promise_1.UnhandledRejectionError(e); | ||
var ure = new Promise_1.UnhandledRejectionError(e, new Trace_1.default()); | ||
expect(ure.reason).to.equal(e); | ||
@@ -453,0 +564,0 @@ }); |
@@ -81,2 +81,3 @@ declare module 'ts-promise/util' { | ||
declare module 'ts-promise/Promise' { | ||
import Trace from 'ts-promise/Trace'; | ||
import BaseError from 'ts-promise/BaseError'; | ||
@@ -88,3 +89,4 @@ export interface Thenable<T> { | ||
reason: any; | ||
constructor(reason: any); | ||
trace: Trace; | ||
constructor(reason: any, trace: Trace); | ||
} | ||
@@ -137,3 +139,3 @@ /** | ||
*/ | ||
resolve: (value?: void) => void; | ||
resolve: (value?: void | Thenable<void>) => void; | ||
} | ||
@@ -156,2 +158,4 @@ export class Promise<T> implements Thenable<T> { | ||
reason(): any; | ||
inspect(): string; | ||
toString(): string; | ||
static resolve<R>(value: R | Thenable<R>): Promise<R>; | ||
@@ -173,3 +177,3 @@ static resolve(): Promise<void>; | ||
private _followThenable(slave, then); | ||
private _enqueue(slave, onFulfilled, onRejected); | ||
private _enqueue(onFulfilled, onRejected, slave, done); | ||
private _flush(); | ||
@@ -183,3 +187,3 @@ private _unwrap(handler); | ||
declare module 'ts-promise/index' { | ||
export { default, Promise, Thenable, UnhandledRejectionError, Deferred } from 'ts-promise/Promise'; | ||
export { default, Promise, Thenable, UnhandledRejectionError, Deferred, VoidDeferred } from 'ts-promise/Promise'; | ||
export { default as BaseError } from 'ts-promise/BaseError'; | ||
@@ -186,0 +190,0 @@ |
{ | ||
"name": "ts-promise", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Fast, robust, type-safe promises", | ||
@@ -5,0 +5,0 @@ "author": "Martin Poelstra <martin@beryllium.net>", |
@@ -34,2 +34,7 @@ <a href="https://promisesaplus.com/"> | ||
If you're using Atom Typescript, this is all you need to get the typings working | ||
in your project too. | ||
Otherwise, add a `<reference path="./node_modules/ts-promise/dist/ts-promise.d.ts" />` | ||
line to your project. Rumor has it that this is no longer necessary when TS 1.6 arrives. | ||
```ts | ||
@@ -58,3 +63,3 @@ // Example using ES6 syntax (e.g. using Typescript or Babel) | ||
``` | ||
42 | ||
"hello world" | ||
Error: my error | ||
@@ -182,2 +187,6 @@ at /home/martin/src/promise-example/example.js:9:35 | ||
Returns rejection reason if rejected, otherwise throws an error. | ||
- `toString(): string` | ||
Returns a human-readable representation of the promise and its status. | ||
- `inspect(): string` | ||
Returns a human-readable representation of the promise and its status. | ||
@@ -213,4 +222,14 @@ # TODO | ||
There currently seems to be an issue with building on Windows, where my | ||
workaround for dts-generator doesn't work. Workaround for the workaround: | ||
(manually) replace the escape-characters ("\n" etc) in dist/ts-promise.d.ts. | ||
A proper fix is in the works. | ||
# Changelog | ||
0.1.4: | ||
- Add longStackTraces support to .done() | ||
- Export VoidDeferred interface and allow resolving it with a Thenable<void> | ||
- Add .toString() and .inspect() | ||
0.1.3: | ||
@@ -217,0 +236,0 @@ - Add Promise.defer() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
251
156997
48
2104