├── .gitignore ├── Makefile ├── Promise.csproj ├── Promise.js ├── Promise.nuspec ├── Promise.sln ├── Promise.ts ├── PromiseTests.js ├── PromiseTests.ts ├── README.md ├── Scripts └── typings │ └── qunit │ └── qunit.d.ts ├── index.html ├── packages.config ├── packages └── repositories.config ├── web.Debug.config ├── web.Release.config └── web.config /.gitignore: -------------------------------------------------------------------------------- 1 | /.vs/ 2 | /bin/ 3 | /obj/ 4 | /promise_all.* 5 | /*.user 6 | /*.nupkg 7 | /*.suo 8 | /packages/ 9 | !/packages/repositories.config 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | id=Promise.TypeScript 2 | version=1.0-rc 3 | package=${id}.${version}.nupkg 4 | 5 | .PHONY: pushnuget 6 | pushnuget: nuget 7 | nuget push ${package} 8 | 9 | .PHONY: nuget 10 | nuget: 11 | nuget pack -Properties "Name=${id};Version=${version}" Promise.nuspec 12 | 13 | -------------------------------------------------------------------------------- /Promise.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | {601A876B-45BD-41C9-AA85-F96ACB4FDB8D} 6 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 7 | Library 8 | bin 9 | v4.0 10 | full 11 | true 12 | true 13 | 14 | 15 | 16 | 17 | ..\ 18 | true 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | web.config 40 | 41 | 42 | web.config 43 | 44 | 45 | 46 | 10.0 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 48 | 49 | 50 | Promise 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | True 59 | True 60 | 0 61 | / 62 | http://localhost:7375/ 63 | False 64 | False 65 | 66 | 67 | False 68 | 69 | 70 | 71 | 72 | 73 | ES5 74 | true 75 | true 76 | AMD 77 | promise_all.js 78 | 79 | 80 | ES5 81 | false 82 | false 83 | AMD 84 | promise_all.js 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Promise.js: -------------------------------------------------------------------------------- 1 | var P; 2 | (function (P) { 3 | function defer() { 4 | return new DeferredI(); 5 | } 6 | P.defer = defer; 7 | function resolve(v) { 8 | if (typeof v === "undefined") { v = {}; } 9 | return defer().resolve(v).promise(); 10 | } 11 | P.resolve = resolve; 12 | function reject(err) { 13 | return defer().reject(err).promise(); 14 | } 15 | P.reject = reject; 16 | function unfold(unspool, seed) { 17 | var d = defer(); 18 | var elements = []; 19 | unfoldCore(elements, d, unspool, seed); 20 | return d.promise(); 21 | } 22 | P.unfold = unfold; 23 | function unfoldCore(elements, deferred, unspool, seed) { 24 | var result = unspool(seed); 25 | if (!result) { 26 | deferred.resolve(elements); 27 | return; 28 | } 29 | while(result.next && result.promise.status == P.Status.Resolved) { 30 | elements.push(result.promise.result); 31 | result = unspool(result.next); 32 | if (!result) { 33 | deferred.resolve(elements); 34 | return; 35 | } 36 | } 37 | result.promise.done(function (v) { 38 | elements.push(v); 39 | if (!result.next) { 40 | deferred.resolve(elements); 41 | } else { 42 | unfoldCore(elements, deferred, unspool, result.next); 43 | } 44 | }).fail(function (e) { 45 | deferred.reject(e); 46 | }); 47 | } 48 | (function (Status) { 49 | Status._map = []; 50 | Status._map[0] = "Unfulfilled"; 51 | Status.Unfulfilled = 0; 52 | Status._map[1] = "Rejected"; 53 | Status.Rejected = 1; 54 | Status._map[2] = "Resolved"; 55 | Status.Resolved = 2; 56 | })(P.Status || (P.Status = {})); 57 | var Status = P.Status; 58 | function when() { 59 | var promises = []; 60 | for (var _i = 0; _i < (arguments.length - 0); _i++) { 61 | promises[_i] = arguments[_i + 0]; 62 | } 63 | var allDone = defer(); 64 | if (!promises.length) { 65 | allDone.resolve([]); 66 | return allDone.promise(); 67 | } 68 | var resolved = 0; 69 | var results = []; 70 | promises.forEach(function (p, i) { 71 | p.done(function (v) { 72 | results[i] = v; 73 | ++resolved; 74 | if (resolved === promises.length && allDone.status != Status.Rejected) { 75 | allDone.resolve(results); 76 | } 77 | }).fail(function (e) { 78 | allDone.reject(new Error("when: one or more promises were rejected")); 79 | }); 80 | }); 81 | return allDone.promise(); 82 | } 83 | P.when = when; 84 | var PromiseI = (function () { 85 | function PromiseI(deferred) { 86 | this.deferred = deferred; 87 | } 88 | Object.defineProperty(PromiseI.prototype, "status", { 89 | get: function () { 90 | return this.deferred.status; 91 | }, 92 | enumerable: true, 93 | configurable: true 94 | }); 95 | Object.defineProperty(PromiseI.prototype, "result", { 96 | get: function () { 97 | return this.deferred.result; 98 | }, 99 | enumerable: true, 100 | configurable: true 101 | }); 102 | Object.defineProperty(PromiseI.prototype, "error", { 103 | get: function () { 104 | return this.deferred.error; 105 | }, 106 | enumerable: true, 107 | configurable: true 108 | }); 109 | PromiseI.prototype.done = function (f) { 110 | this.deferred.done(f); 111 | return this; 112 | }; 113 | PromiseI.prototype.fail = function (f) { 114 | this.deferred.fail(f); 115 | return this; 116 | }; 117 | PromiseI.prototype.always = function (f) { 118 | this.deferred.always(f); 119 | return this; 120 | }; 121 | PromiseI.prototype.then = function (f) { 122 | return this.deferred.then(f); 123 | }; 124 | PromiseI.prototype.thenConvert = function (f) { 125 | return this.deferred.thenConvert(f); 126 | }; 127 | return PromiseI; 128 | })(); 129 | var DeferredI = (function () { 130 | function DeferredI() { 131 | this._resolved = function (_) { 132 | }; 133 | this._rejected = function (_) { 134 | }; 135 | this._status = Status.Unfulfilled; 136 | this._error = { 137 | message: "" 138 | }; 139 | this._promise = new PromiseI(this); 140 | } 141 | DeferredI.prototype.promise = function () { 142 | return this._promise; 143 | }; 144 | Object.defineProperty(DeferredI.prototype, "status", { 145 | get: function () { 146 | return this._status; 147 | }, 148 | enumerable: true, 149 | configurable: true 150 | }); 151 | Object.defineProperty(DeferredI.prototype, "result", { 152 | get: function () { 153 | if (this._status != Status.Resolved) { 154 | throw new Error("Promise: result not available"); 155 | } 156 | return this._result; 157 | }, 158 | enumerable: true, 159 | configurable: true 160 | }); 161 | Object.defineProperty(DeferredI.prototype, "error", { 162 | get: function () { 163 | if (this._status != Status.Rejected) { 164 | throw new Error("Promise: rejection reason not available"); 165 | } 166 | return this._error; 167 | }, 168 | enumerable: true, 169 | configurable: true 170 | }); 171 | DeferredI.prototype.then = function (f) { 172 | var d = defer(); 173 | this.done(function (v) { 174 | var p2 = f(v); 175 | p2.done(function (v2) { 176 | return d.resolve(v2); 177 | }).fail(function (err) { 178 | return d.reject(err); 179 | }); 180 | }).fail(function (err) { 181 | return d.reject(err); 182 | }); 183 | return d.promise(); 184 | }; 185 | DeferredI.prototype.thenConvert = function (f) { 186 | var d = defer(); 187 | this.done(function (v) { 188 | return d.resolve(f(v)); 189 | }).fail(function (err) { 190 | return d.reject(err); 191 | }); 192 | return d.promise(); 193 | }; 194 | DeferredI.prototype.done = function (f) { 195 | if (this.status === Status.Resolved) { 196 | f(this._result); 197 | return this; 198 | } 199 | if (this.status !== Status.Unfulfilled) { 200 | return this; 201 | } 202 | var prev = this._resolved; 203 | this._resolved = function (v) { 204 | prev(v); 205 | f(v); 206 | }; 207 | return this; 208 | }; 209 | DeferredI.prototype.fail = function (f) { 210 | if (this.status === Status.Rejected) { 211 | f(this._error); 212 | return this; 213 | } 214 | if (this.status !== Status.Unfulfilled) { 215 | return this; 216 | } 217 | var prev = this._rejected; 218 | this._rejected = function (e) { 219 | prev(e); 220 | f(e); 221 | }; 222 | return this; 223 | }; 224 | DeferredI.prototype.always = function (f) { 225 | this.done(function (v) { 226 | return f(v); 227 | }).fail(function (err) { 228 | return f(null, err); 229 | }); 230 | return this; 231 | }; 232 | DeferredI.prototype.resolve = function (result) { 233 | if (this._status !== Status.Unfulfilled) { 234 | throw new Error("tried to resolve a fulfilled promise"); 235 | } 236 | this._result = result; 237 | this._status = Status.Resolved; 238 | this._resolved(result); 239 | this.detach(); 240 | return this; 241 | }; 242 | DeferredI.prototype.reject = function (err) { 243 | if (this._status !== Status.Unfulfilled) { 244 | throw new Error("tried to reject a fulfilled promise"); 245 | } 246 | this._error = err; 247 | this._status = Status.Rejected; 248 | this._rejected(err); 249 | this.detach(); 250 | return this; 251 | }; 252 | DeferredI.prototype.detach = function () { 253 | this._resolved = function (_) { 254 | }; 255 | this._rejected = function (_) { 256 | }; 257 | }; 258 | return DeferredI; 259 | })(); 260 | })(P || (P = {})); 261 | -------------------------------------------------------------------------------- /Promise.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $Name$ 5 | $Version$ 6 | pragmatrix 7 | pragmatrix 8 | https://github.com/pragmatrix/Promise 9 | false 10 | Generic Promises for TypeScript 0.9 beta 11 | promises promise futures async-programming asynchronous async callbacks typescript 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Promise.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Promise", "Promise.csproj", "{601A876B-45BD-41C9-AA85-F96ACB4FDB8D}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {601A876B-45BD-41C9-AA85-F96ACB4FDB8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {601A876B-45BD-41C9-AA85-F96ACB4FDB8D}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {601A876B-45BD-41C9-AA85-F96ACB4FDB8D}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {601A876B-45BD-41C9-AA85-F96ACB4FDB8D}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Promise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Module P: Generic Promises for TypeScript 3 | 4 | Project, documentation, and license: https://github.com/pragmatrix/Promise 5 | */ 6 | 7 | module P { 8 | 9 | /** 10 | Returns a new "Deferred" value that may be resolved or rejected. 11 | */ 12 | 13 | export function defer(): Deferred 14 | { 15 | return new DeferredI(); 16 | } 17 | 18 | /** 19 | Converts a value to a resolved promise. 20 | */ 21 | 22 | export function resolve(v: Value): Promise 23 | { 24 | return defer().resolve(v).promise(); 25 | } 26 | 27 | /** 28 | Returns a rejected promise. 29 | */ 30 | 31 | export function reject(err: Rejection): Promise 32 | { 33 | return defer().reject(err).promise(); 34 | } 35 | 36 | /** 37 | http://en.wikipedia.org/wiki/Anamorphism 38 | 39 | Given a seed value, unfold calls the unspool function, waits for the returned promise to be resolved, and then 40 | calls it again if a next seed value was returned. 41 | 42 | All the values of all promise results are collected into the resulting promise which is resolved as soon 43 | the last generated element value is resolved. 44 | */ 45 | 46 | export function unfold( 47 | unspool: (current: Seed) => { promise: Promise; next?: Seed }, 48 | seed: Seed) 49 | : Promise 50 | { 51 | var d = defer(); 52 | var elements: Element[] = new Array(); 53 | 54 | unfoldCore(elements, d, unspool, seed) 55 | 56 | return d.promise(); 57 | } 58 | 59 | function unfoldCore( 60 | elements: Element[], 61 | deferred: Deferred, 62 | unspool: (current: Seed) => { promise: Promise; next?: Seed }, 63 | seed: Seed): void 64 | { 65 | var result = unspool(seed); 66 | if (!result) { 67 | deferred.resolve(elements); 68 | return; 69 | } 70 | 71 | // fastpath: don't waste stack space if promise resolves immediately. 72 | 73 | while (result.next && result.promise.status == P.Status.Resolved) 74 | { 75 | elements.push(result.promise.result); 76 | result = unspool(result.next); 77 | if (!result) { 78 | deferred.resolve(elements); 79 | return; 80 | } 81 | } 82 | 83 | result.promise 84 | .done(v => 85 | { 86 | elements.push(v); 87 | if (!result.next) 88 | deferred.resolve(elements); 89 | else 90 | unfoldCore(elements, deferred, unspool, result.next); 91 | } ) 92 | .fail(e => 93 | { 94 | deferred.reject(e); 95 | } ); 96 | } 97 | 98 | /** 99 | The status of a Promise. Initially a Promise is Unfulfilled and may 100 | change to Rejected or Resolved. 101 | 102 | Once a promise is either Rejected or Resolved, it can not change its 103 | status anymore. 104 | */ 105 | 106 | export enum Status { 107 | Unfulfilled, 108 | Rejected, 109 | Resolved 110 | } 111 | 112 | /** 113 | If a promise gets rejected, at least a message that indicates the error or 114 | reason for the rejection must be provided. 115 | */ 116 | 117 | export interface Rejection 118 | { 119 | message: string; 120 | } 121 | 122 | /** 123 | Both Promise and Deferred share these properties. 124 | */ 125 | 126 | export interface PromiseState 127 | { 128 | /// The current status of the promise. 129 | status: Status; 130 | 131 | /// If the promise got resolved, the result of the promise. 132 | result?: Value; 133 | 134 | /// If the promise got rejected, the rejection message. 135 | error?: Rejection; 136 | } 137 | 138 | /** 139 | A Promise supports basic composition and registration of handlers that are called when the 140 | promise is fulfilled. 141 | 142 | When multiple handlers are registered with done(), fail(), or always(), they are called in the 143 | same order. 144 | */ 145 | 146 | export interface Promise extends PromiseState 147 | { 148 | /** 149 | Returns a promise that represents a promise chain that consists of this 150 | promise and the promise that is returned by the function provided. 151 | The function receives the value of this promise as soon it is resolved. 152 | 153 | If this promise fails, the function is never called and the returned promise 154 | will also fail. 155 | */ 156 | then(f: (v: Value) => Promise): Promise; 157 | then(f: (v: Value) => T2): Promise; 158 | 159 | /// Add a handler that is called when the promise gets resolved. 160 | done(f: (v: Value) => void ): Promise; 161 | /// Add a handler that is called when the promise gets rejected. 162 | fail(f: (err: Rejection) => void ): Promise; 163 | /// Add a handler that is called when the promise gets fulfilled (either resolved or rejected). 164 | always(f: (v?: Value, err?: Rejection) => void ): Promise; 165 | } 166 | 167 | /** 168 | Deferred supports the explicit resolving and rejecting of the 169 | promise and the registration of fulfillment handlers. 170 | 171 | A Deferred should be only visible to the function that initially sets up 172 | an asynchronous process. Callers of that function should only see the Promise that 173 | is returned by promise(). 174 | */ 175 | 176 | export interface Deferred extends PromiseState 177 | { 178 | /// Returns the encapsulated promise of this deferred instance. 179 | /// The returned promise supports composition but removes the ability to resolve or reject 180 | /// the promise. 181 | promise(): Promise; 182 | 183 | /// Resolve the promise. 184 | resolve(result: Value): Deferred; 185 | /// Reject the promise. 186 | reject(err: Rejection): Deferred; 187 | 188 | done(f: (v: Value) => void ): Deferred; 189 | fail(f: (err: Rejection) => void ): Deferred; 190 | always(f: (v?: Value, err?: Rejection) => void ): Deferred; 191 | } 192 | 193 | /** 194 | Creates a promise that gets resolved when all the promises in the argument list get resolved. 195 | As soon one of the arguments gets rejected, the resulting promise gets rejected. 196 | If no promises were provided, the resulting promise is immediately resolved. 197 | */ 198 | 199 | export function when(...promises: Promise[]): Promise 200 | { 201 | var allDone = defer(); 202 | if (!promises.length) { 203 | allDone.resolve([]); 204 | return allDone.promise(); 205 | } 206 | 207 | var resolved = 0; 208 | var results = []; 209 | 210 | promises.forEach((p, i) => { 211 | p 212 | .done(v => { 213 | results[i] = v; 214 | ++resolved; 215 | if (resolved === promises.length && allDone.status !== Status.Rejected) 216 | allDone.resolve(results); 217 | } ) 218 | .fail(e => { 219 | if (allDone.status !== Status.Rejected) 220 | allDone.reject(new Error("when: one or more promises were rejected")); 221 | } ); 222 | } ); 223 | 224 | return allDone.promise(); 225 | } 226 | 227 | /** 228 | Implementation of a promise. 229 | 230 | The Promise instance is a proxy to the Deferred instance. 231 | */ 232 | 233 | class PromiseI implements Promise 234 | { 235 | constructor(public deferred: DeferredI) 236 | { } 237 | 238 | get status(): Status { return this.deferred.status; } 239 | get result(): Value { return this.deferred.result; } 240 | get error(): Rejection { return this.deferred.error; } 241 | 242 | done(f: (v: Value) => void ): Promise { 243 | this.deferred.done(f); 244 | return this; 245 | } 246 | 247 | fail(f: (err: Rejection) => void ): Promise { 248 | this.deferred.fail(f); 249 | return this; 250 | } 251 | 252 | always(f: (v?: Value, err?: Rejection) => void ): Promise { 253 | this.deferred.always(f); 254 | return this; 255 | } 256 | 257 | then(f: (v: Value) => any): Promise 258 | { 259 | return this.deferred.then(f); 260 | } 261 | } 262 | 263 | /** 264 | Implementation of a deferred. 265 | */ 266 | 267 | class DeferredI implements Deferred{ 268 | 269 | private _resolved: (v: Value) => void = _ => { }; 270 | private _rejected: (err: Rejection) => void = _ => { }; 271 | 272 | private _status: Status = Status.Unfulfilled; 273 | private _result: Value; 274 | private _error: Rejection = { message: "" }; 275 | private _promise: Promise; 276 | 277 | constructor() { 278 | this._promise = new PromiseI(this); 279 | } 280 | 281 | promise(): Promise { 282 | return this._promise; 283 | } 284 | 285 | get status(): Status { 286 | return this._status; 287 | } 288 | 289 | get result(): Value { 290 | if (this._status != Status.Resolved) 291 | throw new Error("Promise: result not available"); 292 | return this._result; 293 | } 294 | 295 | get error(): Rejection { 296 | if (this._status != Status.Rejected) 297 | throw new Error("Promise: rejection reason not available"); 298 | return this._error; 299 | } 300 | 301 | then(f: (v: Value) => any): Promise 302 | { 303 | var d = defer(); 304 | 305 | this 306 | .done(v => 307 | { 308 | var promiseOrValue = f(v); 309 | 310 | // todo: need to find another way to check if r is really of interface 311 | // type Promise, otherwise we would not support other 312 | // implementations here. 313 | if (promiseOrValue instanceof PromiseI) 314 | { 315 | var p = > promiseOrValue; 316 | p.done(v2 => d.resolve(v2)) 317 | .fail(err => d.reject(err)); 318 | return p; 319 | } 320 | 321 | d.resolve(promiseOrValue); 322 | } ) 323 | .fail(err => d.reject(err)); 324 | 325 | return d.promise(); 326 | } 327 | 328 | done(f: (v: Value) => void ): Deferred 329 | { 330 | if (this.status === Status.Resolved) { 331 | f(this._result); 332 | return this; 333 | } 334 | 335 | if (this.status !== Status.Unfulfilled) 336 | return this; 337 | 338 | var prev = this._resolved; 339 | this._resolved = v => { prev(v); f(v); } 340 | 341 | return this; 342 | } 343 | 344 | fail(f: (err: Rejection) => void ): Deferred 345 | { 346 | if (this.status === Status.Rejected) { 347 | f(this._error); 348 | return this; 349 | } 350 | 351 | if (this.status !== Status.Unfulfilled) 352 | return this; 353 | 354 | var prev = this._rejected; 355 | this._rejected = e => { prev(e); f(e); } 356 | 357 | return this; 358 | } 359 | 360 | always(f: (v?: Value, err?: Rejection) => void ): Deferred 361 | { 362 | this 363 | .done(v => f(v)) 364 | .fail(err => f(null, err)); 365 | 366 | return this; 367 | } 368 | 369 | resolve(result: Value) { 370 | if (this._status !== Status.Unfulfilled) 371 | throw new Error("tried to resolve a fulfilled promise"); 372 | 373 | this._result = result; 374 | this._status = Status.Resolved; 375 | this._resolved(result); 376 | 377 | this.detach(); 378 | return this; 379 | } 380 | 381 | reject(err: Rejection) { 382 | if (this._status !== Status.Unfulfilled) 383 | throw new Error("tried to reject a fulfilled promise"); 384 | 385 | this._error = err; 386 | this._status = Status.Rejected; 387 | this._rejected(err); 388 | 389 | this.detach(); 390 | return this; 391 | } 392 | 393 | private detach() 394 | { 395 | this._resolved = _ => { }; 396 | this._rejected = _ => { }; 397 | } 398 | } 399 | 400 | /** 401 | Promise Generators and Iterators. 402 | */ 403 | 404 | export interface Generator 405 | { 406 | (): Iterator; 407 | } 408 | 409 | export interface Iterator 410 | { 411 | advance(): Promise; 412 | current: E; 413 | } 414 | 415 | export function generator(g: () => () => Promise): Generator 416 | { 417 | return () => iterator(g()); 418 | }; 419 | 420 | export function iterator(f: () => Promise): Iterator 421 | { 422 | return new IteratorI(f); 423 | } 424 | 425 | class IteratorI implements Iterator 426 | { 427 | current: E = undefined; 428 | 429 | constructor(private f: () => Promise) 430 | { } 431 | 432 | advance() : Promise 433 | { 434 | var res = this.f(); 435 | return res.then(value => 436 | { 437 | if (isUndefined(value)) 438 | return false; 439 | 440 | this.current = value; 441 | return true; 442 | } ); 443 | } 444 | } 445 | 446 | /** 447 | Iterator functions. 448 | */ 449 | 450 | export function each(gen: Generator, f: (e: E) => void ): Promise<{}> 451 | { 452 | var d = defer(); 453 | eachCore(d, gen(), f); 454 | return d.promise(); 455 | } 456 | 457 | function eachCore(fin: Deferred<{}>, it: Iterator, f: (e: E) => void ) : void 458 | { 459 | it.advance() 460 | .done(hasValue => 461 | { 462 | if (!hasValue) 463 | { 464 | fin.resolve({}); 465 | return; 466 | } 467 | 468 | f(it.current) 469 | eachCore(fin, it, f); 470 | } ) 471 | .fail(err => fin.reject(err)); 472 | } 473 | 474 | /** 475 | std 476 | */ 477 | 478 | export function isUndefined(v) 479 | { 480 | return typeof v === 'undefined'; 481 | } 482 | } -------------------------------------------------------------------------------- /PromiseTests.js: -------------------------------------------------------------------------------- 1 | var PromiseTests; 2 | (function (PromiseTests) { 3 | var defer = P.defer; 4 | var when = P.when; 5 | test("when resolve: status===Resolved", function () { 6 | var d = defer(); 7 | d.resolve(0); 8 | ok(d.status === P.Status.Resolved); 9 | }); 10 | test("when reject: status===Resolved", function () { 11 | var d = defer(); 12 | d.reject({ 13 | message: "na" 14 | }); 15 | ok(d.status === P.Status.Rejected); 16 | }); 17 | test("when resolved twice throw", function () { 18 | var d = defer(); 19 | d.resolve(0); 20 | throws(function () { 21 | d.resolve(1); 22 | }); 23 | }); 24 | test("when rejected twice throw", function () { 25 | var d = defer(); 26 | d.reject({ 27 | message: "nana" 28 | }); 29 | throws(function () { 30 | d.reject({ 31 | message: "ohoh" 32 | }); 33 | }); 34 | }); 35 | test("when resolve: done", function () { 36 | var d = defer(); 37 | d.done(function () { 38 | ok(true); 39 | }); 40 | d.resolve(1); 41 | }); 42 | test("when already resolved: done", function () { 43 | var d = defer(); 44 | d.resolve(1); 45 | d.done(function () { 46 | ok(true); 47 | }); 48 | }); 49 | test("when rejected: fail", function () { 50 | var d = defer(); 51 | d.fail(function () { 52 | ok(true); 53 | }); 54 | d.reject({ 55 | message: "na" 56 | }); 57 | }); 58 | test("when already rejected rejected: fail", function () { 59 | var d = defer(); 60 | d.reject({ 61 | message: "na" 62 | }); 63 | d.fail(function () { 64 | ok(true); 65 | }); 66 | }); 67 | test("when rejected, call always", function () { 68 | var d = defer(); 69 | d.reject({ 70 | message: "na" 71 | }); 72 | d.always(function (v, err) { 73 | ok(!v && err.message === "na"); 74 | }); 75 | }); 76 | test("when resolved, call always", function () { 77 | var d = defer(); 78 | d.resolve(10); 79 | d.always(function (v, err) { 80 | ok(!err && v === 10); 81 | }); 82 | }); 83 | test("actually passes the value to the done", function () { 84 | var d = defer(); 85 | d.done(function (value) { 86 | ok(4711 === value); 87 | }); 88 | d.resolve(4711); 89 | }); 90 | test("actually passes the error message to the fail handler", function () { 91 | var d = defer(); 92 | d.fail(function (err) { 93 | ok("nana" === err.message); 94 | }); 95 | d.reject({ 96 | message: "nana" 97 | }); 98 | }); 99 | test("two done handler are called in sequence", function () { 100 | expect(2); 101 | var d = defer(); 102 | var seq = 0; 103 | d.done(function () { 104 | ok(0 === seq++); 105 | }); 106 | d.done(function () { 107 | ok(1 === seq++); 108 | }); 109 | d.resolve(0); 110 | }); 111 | test("two fail handler are called in sequence", function () { 112 | expect(2); 113 | var d = defer(); 114 | var seq = 0; 115 | d.fail(function () { 116 | ok(0 === seq++); 117 | }); 118 | d.fail(function () { 119 | ok(1 === seq++); 120 | }); 121 | d.reject({ 122 | message: "" 123 | }); 124 | }); 125 | test("then receives the value", function () { 126 | var d = defer(); 127 | var d2 = d.promise().then(function (n) { 128 | ok(n === 10); 129 | return defer().promise(); 130 | }); 131 | d.resolve(10); 132 | }); 133 | test("combined resolve", function () { 134 | expect(2); 135 | var d = defer(); 136 | var p = d.promise().then(function (n) { 137 | ok(n === 10); 138 | var d2 = defer(); 139 | d2.resolve(4); 140 | return d2.promise(); 141 | }); 142 | d.resolve(10); 143 | p.done(function (n) { 144 | return ok(n === 4); 145 | }); 146 | }); 147 | test("then: first rejected: first and outer fails", function () { 148 | expect(2); 149 | var first = defer(); 150 | var outer = first.promise().then(function (n) { 151 | ok(false); 152 | return defer().promise(); 153 | }); 154 | first.fail(function (err) { 155 | ok(true); 156 | }); 157 | outer.fail(function (err) { 158 | ok(true); 159 | }); 160 | first.reject({ 161 | message: "fail" 162 | }); 163 | }); 164 | test("then: first resolved and second rejected: second fails and outer fails", function () { 165 | expect(2); 166 | var first = defer(); 167 | var second = defer(); 168 | var outer = first.promise().then(function (n) { 169 | return second.promise(); 170 | }); 171 | second.fail(function (err) { 172 | ok(true); 173 | }); 174 | outer.fail(function (err) { 175 | ok(true); 176 | }); 177 | first.resolve(1); 178 | second.reject({ 179 | message: "fail" 180 | }); 181 | }); 182 | test("when no arguments given, when resolves immediately", function () { 183 | var p = when(); 184 | p.done(function (v) { 185 | ok(!v.length); 186 | }); 187 | }); 188 | test("when all are resolved, when resolves", function () { 189 | expect(3); 190 | var d = defer(); 191 | var d2 = defer(); 192 | var p = when(d.promise(), d2.promise()); 193 | p.done(function (v) { 194 | ok(v.length === 2); 195 | ok(v[0] === 10); 196 | ok(v[1] === true); 197 | }); 198 | d.resolve(10); 199 | d2.resolve(true); 200 | }); 201 | test("when first fails, when fails", function () { 202 | var d = defer(); 203 | var d2 = defer(); 204 | var p = when(d.promise(), d2.promise()); 205 | p.fail(function (err) { 206 | ok(true); 207 | }); 208 | d.reject({ 209 | message: "nana" 210 | }); 211 | }); 212 | test("when second fails, when fails", function () { 213 | var d = defer(); 214 | var d2 = defer(); 215 | var p = when(d.promise(), d2.promise()); 216 | p.fail(function (err) { 217 | ok(true); 218 | }); 219 | d2.reject({ 220 | message: "nana" 221 | }); 222 | }); 223 | test("accessing the result property of a resolved promise does not throw", function () { 224 | var p = P.resolve(1); 225 | ok(1 === p.result); 226 | }); 227 | test("accessing the result of an unfulfilled or rejected promise throws", function () { 228 | var d = defer(); 229 | var p = d.promise(); 230 | throws(function () { 231 | p.result; 232 | }); 233 | d.reject({ 234 | message: "no!" 235 | }); 236 | throws(function () { 237 | p.result; 238 | }); 239 | }); 240 | test("accessing the error property of a rejected promise does not throw", function () { 241 | var d = defer(); 242 | d.reject({ 243 | message: "rejected" 244 | }); 245 | ok(d.error.message === "rejected"); 246 | }); 247 | test("accessing the error property of an unfulfilled or resolved promise throws", function () { 248 | var d = defer(); 249 | var p = d.promise(); 250 | throws(function () { 251 | p.error; 252 | }); 253 | d.resolve(1); 254 | throws(function () { 255 | p.error; 256 | }); 257 | }); 258 | })(PromiseTests || (PromiseTests = {})); 259 | -------------------------------------------------------------------------------- /PromiseTests.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module PromiseTests { 5 | 6 | var defer = P.defer; 7 | var when = P.when; 8 | interface Promise extends P.Promise {} 9 | 10 | // state 11 | 12 | test("when resolve: status===Resolved", () => 13 | { 14 | var d = defer(); 15 | d.resolve(0); 16 | ok(d.status === P.Status.Resolved); 17 | } ); 18 | 19 | test("when reject: status===Resolved", () => 20 | { 21 | var d = defer(); 22 | d.reject({ message: "na" }); 23 | ok(d.status === P.Status.Rejected); 24 | } ); 25 | 26 | // double resolve reject 27 | 28 | test("when resolved twice throw", () => 29 | { 30 | var d = defer(); 31 | d.resolve(0); 32 | throws(() => 33 | { 34 | d.resolve(1); 35 | } ); 36 | } ); 37 | 38 | test("when rejected twice throw", () => 39 | { 40 | var d = defer(); 41 | d.reject({ message: "nana" }); 42 | 43 | throws(() => 44 | { 45 | d.reject({ message: "ohoh" }); 46 | } ); 47 | } ); 48 | 49 | // done / fail / always handlers 50 | 51 | test("when resolve: done", () => 52 | { 53 | var d = defer(); 54 | d.done(() => { 55 | ok(true); 56 | } ); 57 | 58 | d.resolve(1); 59 | } ); 60 | 61 | test("when already resolved: done", () => 62 | { 63 | var d = defer(); 64 | d.resolve(1); 65 | d.done(() => { 66 | ok(true); 67 | } ); 68 | } ); 69 | 70 | test("when rejected: fail", () => 71 | { 72 | var d = defer(); 73 | d.fail(() => { 74 | ok(true); 75 | } ); 76 | d.reject({ message: "na" }); 77 | } ); 78 | 79 | test("when already rejected: fail", () => 80 | { 81 | var d = defer(); 82 | d.reject({ message: "na" }); 83 | d.fail(() => { 84 | ok(true); 85 | } ); 86 | } ); 87 | 88 | test("when rejected, always call", () => 89 | { 90 | var d = defer(); 91 | d.reject({ message: "na" }); 92 | d.always((v?, err?) => { 93 | ok(!v && err.message === "na"); 94 | } ); 95 | } ); 96 | 97 | test("when resolved, always call", () => 98 | { 99 | var d = defer(); 100 | d.resolve(10); 101 | d.always((v?, err?) => { 102 | ok(!err && v === 10); 103 | } ); 104 | } ); 105 | 106 | // values and errors 107 | 108 | test("actually passes the value to done", () => 109 | { 110 | var d = defer(); 111 | d.done((value) => { 112 | ok(4711===value); 113 | } ); 114 | d.resolve(4711); 115 | } ); 116 | 117 | test("actually passes the error message to the fail handler", () => 118 | { 119 | var d = defer(); 120 | d.fail((err) => { 121 | ok("nana" === err.message); 122 | } ); 123 | d.reject({ message: "nana" }); 124 | } ); 125 | 126 | // handler sequencing 127 | 128 | test("two done handler are called in sequence", () => 129 | { 130 | expect(2); 131 | 132 | var d = defer(); 133 | var seq = 0; 134 | 135 | d.done(() => 136 | { 137 | ok(0 === seq++); 138 | } ); 139 | 140 | d.done(() => 141 | { 142 | ok(1 === seq++); 143 | } ); 144 | 145 | d.resolve(0); 146 | } ); 147 | 148 | test("two fail handler are called in sequence", () => 149 | { 150 | expect(2); 151 | 152 | var d = defer(); 153 | var seq = 0; 154 | 155 | d.fail(() => 156 | { 157 | ok(0 === seq++); 158 | } ); 159 | 160 | d.fail(() => 161 | { 162 | ok(1 === seq++); 163 | } ); 164 | 165 | d.reject({ message: "" }); 166 | } ); 167 | 168 | // then 169 | 170 | test("then receives the value", () => 171 | { 172 | var d = defer(); 173 | var d2 = d.promise().then(n => { 174 | ok(n === 10); 175 | return defer().promise(); 176 | }); 177 | 178 | d.resolve(10); 179 | } ); 180 | 181 | test("combined resolve", () => 182 | { 183 | expect(2); 184 | 185 | var d = defer(); 186 | var p = d.promise().then(n => { 187 | ok(n === 10); 188 | var d2 = defer(); 189 | d2.resolve(4); 190 | return d2.promise(); 191 | } ); 192 | 193 | d.resolve(10); 194 | p.done(n => ok(n === 4)); 195 | } ); 196 | 197 | 198 | test("then: first rejected: first and outer fails", () => 199 | { 200 | expect(2); 201 | 202 | var first = defer(); 203 | var outer = first.promise().then(n => { 204 | ok(false); 205 | return defer().promise(); 206 | } ); 207 | 208 | first.fail(err => 209 | { 210 | ok(true); 211 | } ); 212 | 213 | outer.fail(err => 214 | { 215 | ok(true); 216 | } ); 217 | 218 | first.reject({ message: "fail" }); 219 | } ); 220 | 221 | test("then: first resolved and second rejected: second fails and outer fails", () => 222 | { 223 | expect(2); 224 | 225 | var first = defer(); 226 | var second = defer(); 227 | 228 | var outer = first.promise().then(n => { 229 | return second.promise(); 230 | } ); 231 | 232 | second.fail(err => 233 | { 234 | ok(true); 235 | } ); 236 | 237 | outer.fail(err => 238 | { 239 | ok(true); 240 | } ); 241 | 242 | first.resolve(1); 243 | second.reject({ message: "fail" }); 244 | } ); 245 | 246 | 247 | test("then: then with a resolved value calls the right overload", () => 248 | { 249 | var first = defer(); 250 | var outer = first.promise().then(v => 4); 251 | 252 | outer.done(x => 253 | { 254 | ok(x == 4); 255 | } ); 256 | 257 | first.resolve(1); 258 | }) 259 | 260 | // when 261 | 262 | test("when: no arguments given, when resolves immediately", () => 263 | { 264 | var p = when(); 265 | p.done(v => { 266 | ok(!v.length); 267 | } ); 268 | } ); 269 | 270 | test("when: all are resolved, when resolves", () => 271 | { 272 | expect(3); 273 | 274 | var d = defer(); 275 | var d2 = defer(); 276 | 277 | var p = when(d.promise(), d2.promise()); 278 | p.done(v => 279 | { 280 | ok(v.length === 2); 281 | ok(v[0] === 10); 282 | ok(v[1] === true); 283 | } ); 284 | 285 | d.resolve(10); 286 | d2.resolve(true); 287 | } ); 288 | 289 | test("when: first fails, when fails", () => 290 | { 291 | var d = defer(); 292 | var d2 = defer(); 293 | 294 | var p = when(d.promise(), d2.promise()); 295 | p.fail(err => 296 | { 297 | ok(true); 298 | } ); 299 | 300 | d.reject({ message: "nana" }); 301 | } ); 302 | 303 | test("when: second fails, when fails", () => 304 | { 305 | var d = defer(); 306 | var d2 = defer(); 307 | 308 | var p = when(d.promise(), d2.promise()); 309 | p.fail(err => 310 | { 311 | ok(true); 312 | } ); 313 | 314 | d2.reject({ message: "nana" }); 315 | } ); 316 | 317 | test("when: both fail, when fails", () => 318 | { 319 | var d = defer(); 320 | var d2 = defer(); 321 | 322 | var p = when(d.promise(), d2.promise()); 323 | p.fail(err => 324 | { 325 | ok(true); 326 | } ); 327 | 328 | d.reject({ message: "nana" }); 329 | d2.reject({ message: "nana" }); 330 | } ); 331 | 332 | // result / error 333 | 334 | test("accessing the result property of a resolved promise does not throw", () => 335 | { 336 | var p = P.resolve(1); 337 | ok(1 === p.result); 338 | } ); 339 | 340 | test("accessing the result of an unfulfilled or rejected promise throws", () => 341 | { 342 | var d = defer(); 343 | var p = d.promise(); 344 | throws(() => 345 | { 346 | p.result; 347 | } ); 348 | 349 | d.reject({ message: "no!" }); 350 | 351 | throws(() => 352 | { 353 | p.result; 354 | } ); 355 | } ); 356 | 357 | 358 | test("accessing the error property of a rejected promise does not throw", () => 359 | { 360 | var d = defer(); 361 | d.reject({ message: "rejected" }); 362 | 363 | ok(d.error.message === "rejected"); 364 | } ); 365 | 366 | test("accessing the error property of an unfulfilled or resolved promise throws", () => 367 | { 368 | var d = defer(); 369 | var p = d.promise(); 370 | throws(() => 371 | { 372 | p.error; 373 | } ); 374 | 375 | d.resolve(1); 376 | 377 | throws(() => 378 | { 379 | p.error; 380 | } ); 381 | } ); 382 | 383 | } 384 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A Generic Promise implementation for TypeScript 2 | 3 | Promises are used to structure asynchronous programs and to avoid callback hell. While they are not as full featured like [Rx](https://github.com/Reactive-Extensions/RxJS) for example, they are attractive because their implementation is rather simple. 4 | 5 | ### Installing Promise.ts 6 | 7 | Assuming that you are using Visual Studio, have [TypeScript 1.0rc](http://www.typescriptlang.org) installed: 8 | 9 | - [install the nuget package](http://nuget.org/packages/Promise.TypeScript/). 10 | 11 | - Then refer the Promise.ts file like: 12 | 13 | /// 14 | 15 | - and to your module: 16 | 17 | export var defer = P.defer; 18 | export var when = P.when; 19 | export interface Promise extends P.Promise {} 20 | 21 | ### Using Promise.ts 22 | 23 | #### Creating Functions that return Promises 24 | 25 | The pattern for creating promises is as follows: First a "deferred" is created that represent a precursor to a promise, then the deferred is bound to the callbacks, and then it returns the promise which may then be composed by the caller. 26 | 27 | For example: 28 | 29 | function readBlob(blob: Blob): Promise 30 | { 31 | var d = defer(); 32 | 33 | var reader = new FileReader(); 34 | 35 | reader.onerror = (err) => d.reject(err); 36 | reader.onabort = () => { 37 | d.reject({ message: "aborted" }); 38 | } 39 | reader.onloadend = () => { 40 | d.resolve(reader.result); 41 | } 42 | 43 | reader.readAsArrayBuffer(blob); 44 | 45 | return d.promise(); 46 | } 47 | 48 | The function above starts the reading operation and returns a promise that represents the future result. 49 | 50 | #### Composing Promises 51 | 52 | The promise returned by `readBlob()` can now be composed with other functions that return promises: 53 | 54 | readBlob(blob).then(bytes => writeFile("name", bytes)); 55 | 56 | `then` also accepts regular values which act like an already resolved promise, for example 57 | 58 | readBlob(blob).then(bytes => bytes.reverse()); 59 | 60 | returns a promise that represents the read operation of the block and the reversing of its binary content. The returned promise gets resolved as soon the conversion function finishes. 61 | 62 | Starting parallel processes is also straight forward: 63 | 64 | var blobReader = readBlob(blob); 65 | var f1 = blobReader.then(bytes => writeFile("name", bytes)); 66 | var f2 = blobReader.then(bytes => writeFile("name2", bytes)); 67 | 68 | Would then write the received bytes to the file "name" and "name2" at the same time. 69 | 70 | Note that it is also possible to retrieve the result directly from the `blobReader` instance: 71 | 72 | var f1 = blobReader.then(() => writeFile("name1", blobReader.result)); 73 | 74 | .. which may simplify complex composition scenarios. 75 | 76 | And when we need to join the results together, the `when` combinator takes a number of promises and returns one that resolves when all its arguments are resolved: 77 | 78 | when(f1, f2).then(() => commitToDatabaseThatAllFilesAreWritten()); 79 | 80 | Note that the returning type of `when` is `Promise`, which resolves to an array that contains the results `when` was waiting for. 81 | 82 | #### Adding Handlers 83 | 84 | In addition to the composition features, there are low level primitive functions that can be used to register certain handlers: 85 | 86 | p.done(value => {}); 87 | 88 | registers a handler that is called when the promise is resolved. 89 | 90 | p.fail(err => {}); 91 | 92 | registers a handler that is called when the promise is rejected. 93 | 94 | p.always((value?, err?) => {}); 95 | 96 | registers a handler that is called when either the promise is resolved or rejected. 97 | 98 | When multiple handlers of the same kind are registered, they are called in their registration order. 99 | 100 | ### Ideas 101 | 102 | - An explicit parallel combinator. 103 | - Instead of `any[]`, use Tuples for the return type of `when()`. Because TypeScript does not have a standard library, someone needs to create `Tuple<>` types. BTW: What about creating a standard library that can be distributed as a number of nuget packages similar to the [DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped) repository? 104 | - Some looping or recursing construct that avoids eating stack space. The [recur special form](http://clojure.org/special_forms#recur) in Clojure looks like a good idea to start from. 105 | - Progress notifications anyone? 106 | - Exception handling. 107 | 108 | ### Design & Implementation Details 109 | 110 | I've decided to make it a bit harder to compose deferreds by not adding the composition methods to the `Deferred` interface. This makes the implementation simpler and has the additional advantage that implementors need to think and explicitly call `promise()` before they can start composing. 111 | 112 | I've decided against all exception handling for now, because I have never used promises before and don't know what exactly programmers would expect. Obviously this might change for a future version. 113 | 114 | ### License 115 | 116 | Copyright (c) 2014, Armin Sander All rights reserved. 117 | 118 | - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 119 | 120 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 121 | 122 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 123 | 124 | Neither the name of Armin Sander nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 125 | 126 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARMIN SANDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 127 | -------------------------------------------------------------------------------- /Scripts/typings/qunit/qunit.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for QUnit 1.10 2 | // Project: http://qunitjs.com/ 3 | // Definitions by: Diullei Gomes 4 | // DefinitelyTyped: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | 7 | interface DoneCallbackObject { 8 | /** 9 | * The number of failed assertions 10 | */ 11 | failed: number; 12 | 13 | /** 14 | * The number of passed assertions 15 | */ 16 | passed: number; 17 | 18 | /** 19 | * The total number of assertions 20 | */ 21 | total: number; 22 | 23 | /** 24 | * The time in milliseconds it took tests to run from start to finish. 25 | */ 26 | runtime: number; 27 | } 28 | 29 | interface LogCallbackObject { 30 | /** 31 | * The boolean result of an assertion, true means passed, false means failed. 32 | */ 33 | result: boolean; 34 | 35 | /** 36 | * One side of a comparision assertion. Can be undefined when ok() is used. 37 | */ 38 | actual: Object; 39 | 40 | /** 41 | * One side of a comparision assertion. Can be undefined when ok() is used. 42 | */ 43 | expected: Object; 44 | 45 | /** 46 | * A string description provided by the assertion. 47 | */ 48 | message: string; 49 | 50 | /** 51 | * The associated stacktrace, either from an exception or pointing to the source 52 | * of the assertion. Depends on browser support for providing stacktraces, so can be 53 | * undefined. 54 | */ 55 | source: string; 56 | } 57 | 58 | interface ModuleStartCallbackObject { 59 | /** 60 | * Name of the next module to run 61 | */ 62 | name: string; 63 | } 64 | 65 | interface ModuleDoneCallbackObject { 66 | /** 67 | * Name of this module 68 | */ 69 | name: string; 70 | 71 | /** 72 | * The number of failed assertions 73 | */ 74 | failed: number; 75 | 76 | /** 77 | * The number of passed assertions 78 | */ 79 | passed: number; 80 | 81 | /** 82 | * The total number of assertions 83 | */ 84 | total: number; 85 | } 86 | 87 | interface TestDoneCallbackObject { 88 | /** 89 | * TName of the next test to run 90 | */ 91 | name: string; 92 | 93 | /** 94 | * Name of the current module 95 | */ 96 | module: string; 97 | 98 | /** 99 | * The number of failed assertions 100 | */ 101 | failed: number; 102 | 103 | /** 104 | * The number of passed assertions 105 | */ 106 | passed: number; 107 | 108 | /** 109 | * The total number of assertions 110 | */ 111 | total: number; 112 | 113 | /** 114 | * The total runtime, including setup and teardown 115 | */ 116 | duration: number; 117 | } 118 | 119 | interface TestStartCallbackObject { 120 | /** 121 | * Name of the next test to run 122 | */ 123 | name: string; 124 | 125 | /** 126 | * Name of the current module 127 | */ 128 | module: string; 129 | } 130 | 131 | interface Config { 132 | altertitle: boolean; 133 | autostart: boolean; 134 | current: Object; 135 | reorder: boolean; 136 | requireExpects: boolean; 137 | urlConfig: any[]; 138 | done: any; 139 | } 140 | 141 | interface LifecycleObject { 142 | /** 143 | * Runs before each test 144 | */ 145 | setup?: () => any; 146 | 147 | /** 148 | * Runs after each test 149 | */ 150 | teardown?: () => any; 151 | } 152 | 153 | interface QUnitAssert { 154 | /* ASSERT */ 155 | assert: any; 156 | current_testEnvironment: any; 157 | jsDump: any; 158 | 159 | /** 160 | * A deep recursive comparison assertion, working on primitive types, arrays, objects, 161 | * regular expressions, dates and functions. 162 | * 163 | * The deepEqual() assertion can be used just like equal() when comparing the value of 164 | * objects, such that { key: value } is equal to { key: value }. For non-scalar values, 165 | * identity will be disregarded by deepEqual. 166 | * 167 | * @param actual Object or Expression being tested 168 | * @param expected Known comparison value 169 | * @param message A short description of the assertion 170 | */ 171 | deepEqual(actual: any, expected: any, message?: string); 172 | 173 | /** 174 | * A non-strict comparison assertion, roughly equivalent to JUnit assertEquals. 175 | * 176 | * The equal assertion uses the simple comparison operator (==) to compare the actual 177 | * and expected arguments. When they are equal, the assertion passes; otherwise, it fails. 178 | * When it fails, both actual and expected values are displayed in the test result, 179 | * in addition to a given message. 180 | * 181 | * @param actual Expression being tested 182 | * @param expected Known comparison value 183 | * @param message A short description of the assertion 184 | */ 185 | equal(actual: any, expected: any, message?: string); 186 | 187 | /** 188 | * An inverted deep recursive comparison assertion, working on primitive types, 189 | * arrays, objects, regular expressions, dates and functions. 190 | * 191 | * The notDeepEqual() assertion can be used just like equal() when comparing the 192 | * value of objects, such that { key: value } is equal to { key: value }. For non-scalar 193 | * values, identity will be disregarded by notDeepEqual. 194 | * 195 | * @param actual Object or Expression being tested 196 | * @param expected Known comparison value 197 | * @param message A short description of the assertion 198 | */ 199 | notDeepEqual(actual: any, expected: any, message?: string); 200 | 201 | /** 202 | * A non-strict comparison assertion, checking for inequality. 203 | * 204 | * The notEqual assertion uses the simple inverted comparison operator (!=) to compare 205 | * the actual and expected arguments. When they aren't equal, the assertion passes; 206 | * otherwise, it fails. When it fails, both actual and expected values are displayed 207 | * in the test result, in addition to a given message. 208 | * 209 | * @param actual Expression being tested 210 | * @param expected Known comparison value 211 | * @param message A short description of the assertion 212 | */ 213 | notEqual(actual: any, expected: any, message?: string); 214 | 215 | notPropEqual(actual: any, expected: any, message?: string); 216 | 217 | propEqual(actual: any, expected: any, message?: string); 218 | 219 | /** 220 | * A non-strict comparison assertion, checking for inequality. 221 | * 222 | * The notStrictEqual assertion uses the strict inverted comparison operator (!==) 223 | * to compare the actual and expected arguments. When they aren't equal, the assertion 224 | * passes; otherwise, it fails. When it fails, both actual and expected values are 225 | * displayed in the test result, in addition to a given message. 226 | * 227 | * @param actual Expression being tested 228 | * @param expected Known comparison value 229 | * @param message A short description of the assertion 230 | */ 231 | notStrictEqual(actual: any, expected: any, message?: string); 232 | 233 | /** 234 | * A boolean assertion, equivalent to CommonJS’s assert.ok() and JUnit’s assertTrue(). 235 | * Passes if the first argument is truthy. 236 | * 237 | * The most basic assertion in QUnit, ok() requires just one argument. If the argument 238 | * evaluates to true, the assertion passes; otherwise, it fails. If a second message 239 | * argument is provided, it will be displayed in place of the result. 240 | * 241 | * @param state Expression being tested 242 | * @param message A short description of the assertion 243 | */ 244 | ok(state: any, message?: string); 245 | 246 | /** 247 | * A strict type and value comparison assertion. 248 | * 249 | * The strictEqual() assertion provides the most rigid comparison of type and value with 250 | * the strict equality operator (===) 251 | * 252 | * @param actual Expression being tested 253 | * @param expected Known comparison value 254 | * @param message A short description of the assertion 255 | */ 256 | strictEqual(actual: any, expected: any, message?: string); 257 | 258 | /** 259 | * Assertion to test if a callback throws an exception when run. 260 | * 261 | * When testing code that is expected to throw an exception based on a specific set of 262 | * circumstances, use throws() to catch the error object for testing and comparison. 263 | * 264 | * @param block Function to execute 265 | * @param expected Error Object to compare 266 | * @param message A short description of the assertion 267 | */ 268 | throws(block: () => any, expected: any, message?: string); 269 | 270 | /** 271 | * @param block Function to execute 272 | * @param message A short description of the assertion 273 | */ 274 | throws(block: () => any, message?: string); 275 | } 276 | 277 | interface QUnitStatic extends QUnitAssert{ 278 | /* ASYNC CONTROL */ 279 | 280 | /** 281 | * Start running tests again after the testrunner was stopped. See stop(). 282 | * 283 | * When your async test has multiple exit points, call start() for the corresponding number of stop() increments. 284 | * 285 | * @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls. 286 | */ 287 | start(decrement?: number); 288 | 289 | /** 290 | * Stop the testrunner to wait for async tests to run. Call start() to continue. 291 | * 292 | * When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need. 293 | * 294 | * On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere. 295 | * 296 | * @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls. 297 | */ 298 | stop(increment? : number); 299 | 300 | /* CALLBACKS */ 301 | 302 | /** 303 | * Register a callback to fire whenever the test suite begins. 304 | * 305 | * QUnit.begin() is called once before running any tests. (a better would've been QUnit.start, 306 | * but thats already in use elsewhere and can't be changed.) 307 | * 308 | * @param callback Callback to execute 309 | */ 310 | begin(callback: () => any); 311 | 312 | /** 313 | * Register a callback to fire whenever the test suite ends. 314 | * 315 | * @param callback Callback to execute. 316 | */ 317 | done(callback: (details: DoneCallbackObject) => any); 318 | 319 | /** 320 | * Register a callback to fire whenever an assertion completes. 321 | * 322 | * This is one of several callbacks QUnit provides. Its intended for integration scenarios like 323 | * PhantomJS or Jenkins. The properties of the details argument are listed below as options. 324 | * 325 | * @param callback Callback to execute. 326 | */ 327 | log(callback: (details: LogCallbackObject) => any); 328 | 329 | /** 330 | * Register a callback to fire whenever a module ends. 331 | * 332 | * @param callback Callback to execute. 333 | */ 334 | moduleDone(callback: (details: ModuleDoneCallbackObject) => any); 335 | 336 | /** 337 | * Register a callback to fire whenever a module begins. 338 | * 339 | * @param callback Callback to execute. 340 | */ 341 | moduleStart(callback: (details: ModuleStartCallbackObject) => any); 342 | 343 | /** 344 | * Register a callback to fire whenever a test ends. 345 | * 346 | * @param callback Callback to execute. 347 | */ 348 | testDone(callback: (details: TestDoneCallbackObject) => any); 349 | 350 | /** 351 | * Register a callback to fire whenever a test begins. 352 | * 353 | * @param callback Callback to execute. 354 | */ 355 | testStart(callback: (details: TestStartCallbackObject) => any); 356 | 357 | /* CONFIGURATION */ 358 | 359 | /** 360 | * QUnit has a bunch of internal configuration defaults, some of which are 361 | * useful to override. Check the description for each option for details. 362 | */ 363 | config: Config; 364 | 365 | /* TEST */ 366 | 367 | /** 368 | * Add an asynchronous test to run. The test must include a call to start(). 369 | * 370 | * For testing asynchronous code, asyncTest will automatically stop the test runner 371 | * and wait for your code to call start() to continue. 372 | * 373 | * @param name Title of unit being tested 374 | * @param expected Number of assertions in this test 375 | * @param test Function to close over assertions 376 | */ 377 | asyncTest(name: string, expected: number, test: () => any); 378 | 379 | /** 380 | * Add an asynchronous test to run. The test must include a call to start(). 381 | * 382 | * For testing asynchronous code, asyncTest will automatically stop the test runner 383 | * and wait for your code to call start() to continue. 384 | * 385 | * @param name Title of unit being tested 386 | * @param test Function to close over assertions 387 | */ 388 | asyncTest(name: string, test: () => any); 389 | 390 | /** 391 | * Specify how many assertions are expected to run within a test. 392 | * 393 | * To ensure that an explicit number of assertions are run within any test, use 394 | * expect( number ) to register an expected count. If the number of assertions 395 | * run does not match the expected count, the test will fail. 396 | * 397 | * @param amount Number of assertions in this test. 398 | */ 399 | expect(amount: number); 400 | 401 | /** 402 | * Group related tests under a single label. 403 | * 404 | * All tests that occur after a call to module() will be grouped into that module. 405 | * The test names will all be preceded by the module name in the test results. 406 | * You can then use that module name to select tests to run. 407 | * 408 | * @param name Label for this group of tests 409 | * @param lifecycle Callbacks to run before and after each test 410 | */ 411 | module(name: string, lifecycle?: LifecycleObject); 412 | 413 | /** 414 | * Add a test to run. 415 | * 416 | * When testing the most common, synchronous code, use test(). 417 | * The assert argument to the callback contains all of QUnit's assertion methods. 418 | * If you are avoiding using any of QUnit's globals, you can use the assert 419 | * argument instead. 420 | * 421 | * @param title Title of unit being tested 422 | * @param expected Number of assertions in this test 423 | * @param test Function to close over assertions 424 | */ 425 | test(title: string, expected: number, test: (assert: QUnitAssert) => any); 426 | 427 | /** 428 | * @param title Title of unit being tested 429 | * @param test Function to close over assertions 430 | */ 431 | test(title: string, test: (assert: QUnitAssert) => any); 432 | 433 | /** 434 | * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568 435 | */ 436 | equiv(a: any, b: any); 437 | 438 | // https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L661 439 | raises: any; 440 | 441 | /** 442 | * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L897 443 | */ 444 | push(result, actual, expected, message): any; 445 | 446 | /** 447 | * https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L839 448 | */ 449 | reset(): any; 450 | } 451 | 452 | /* ASSERT */ 453 | 454 | /** 455 | * A deep recursive comparison assertion, working on primitive types, arrays, objects, 456 | * regular expressions, dates and functions. 457 | * 458 | * The deepEqual() assertion can be used just like equal() when comparing the value of 459 | * objects, such that { key: value } is equal to { key: value }. For non-scalar values, 460 | * identity will be disregarded by deepEqual. 461 | * 462 | * @param actual Object or Expression being tested 463 | * @param expected Known comparison value 464 | * @param message A short description of the assertion 465 | */ 466 | declare function deepEqual(actual: any, expected: any, message?: string); 467 | 468 | /** 469 | * A non-strict comparison assertion, roughly equivalent to JUnit assertEquals. 470 | * 471 | * The equal assertion uses the simple comparison operator (==) to compare the actual 472 | * and expected arguments. When they are equal, the assertion passes; otherwise, it fails. 473 | * When it fails, both actual and expected values are displayed in the test result, 474 | * in addition to a given message. 475 | * 476 | * @param actual Expression being tested 477 | * @param expected Known comparison value 478 | * @param message A short description of the assertion 479 | */ 480 | declare function equal(actual: any, expected: any, message?: string); 481 | 482 | /** 483 | * An inverted deep recursive comparison assertion, working on primitive types, 484 | * arrays, objects, regular expressions, dates and functions. 485 | * 486 | * The notDeepEqual() assertion can be used just like equal() when comparing the 487 | * value of objects, such that { key: value } is equal to { key: value }. For non-scalar 488 | * values, identity will be disregarded by notDeepEqual. 489 | * 490 | * @param actual Object or Expression being tested 491 | * @param expected Known comparison value 492 | * @param message A short description of the assertion 493 | */ 494 | declare function notDeepEqual(actual: any, expected: any, message?: string); 495 | 496 | /** 497 | * A non-strict comparison assertion, checking for inequality. 498 | * 499 | * The notEqual assertion uses the simple inverted comparison operator (!=) to compare 500 | * the actual and expected arguments. When they aren't equal, the assertion passes; 501 | * otherwise, it fails. When it fails, both actual and expected values are displayed 502 | * in the test result, in addition to a given message. 503 | * 504 | * @param actual Expression being tested 505 | * @param expected Known comparison value 506 | * @param message A short description of the assertion 507 | */ 508 | declare function notEqual(actual: any, expected: any, message?: string); 509 | 510 | /** 511 | * A non-strict comparison assertion, checking for inequality. 512 | * 513 | * The notStrictEqual assertion uses the strict inverted comparison operator (!==) 514 | * to compare the actual and expected arguments. When they aren't equal, the assertion 515 | * passes; otherwise, it fails. When it fails, both actual and expected values are 516 | * displayed in the test result, in addition to a given message. 517 | * 518 | * @param actual Expression being tested 519 | * @param expected Known comparison value 520 | * @param message A short description of the assertion 521 | */ 522 | declare function notStrictEqual(actual: any, expected: any, message?: string); 523 | 524 | /** 525 | * A boolean assertion, equivalent to CommonJS’s assert.ok() and JUnit’s assertTrue(). 526 | * Passes if the first argument is truthy. 527 | * 528 | * The most basic assertion in QUnit, ok() requires just one argument. If the argument 529 | * evaluates to true, the assertion passes; otherwise, it fails. If a second message 530 | * argument is provided, it will be displayed in place of the result. 531 | * 532 | * @param state Expression being tested 533 | * @param message A short description of the assertion 534 | */ 535 | declare function ok(state: any, message?: string); 536 | 537 | /** 538 | * A strict type and value comparison assertion. 539 | * 540 | * The strictEqual() assertion provides the most rigid comparison of type and value with 541 | * the strict equality operator (===) 542 | * 543 | * @param actual Expression being tested 544 | * @param expected Known comparison value 545 | * @param message A short description of the assertion 546 | */ 547 | declare function strictEqual(actual: any, expected: any, message?: string); 548 | 549 | /** 550 | * Assertion to test if a callback throws an exception when run. 551 | * 552 | * When testing code that is expected to throw an exception based on a specific set of 553 | * circumstances, use throws() to catch the error object for testing and comparison. 554 | * 555 | * @param block Function to execute 556 | * @param expected Error Object to compare 557 | * @param message A short description of the assertion 558 | */ 559 | declare function throws(block: () => any, expected: any, message?: string); 560 | 561 | /** 562 | * @param block Function to execute 563 | * @param message A short description of the assertion 564 | */ 565 | declare function throws(block: () => any, message?: string); 566 | 567 | /* ASYNC CONTROL */ 568 | 569 | /** 570 | * Start running tests again after the testrunner was stopped. See stop(). 571 | * 572 | * When your async test has multiple exit points, call start() for the corresponding number of stop() increments. 573 | * 574 | * @param decrement Optional argument to merge multiple start() calls into one. Use with multiple corrsponding stop() calls. 575 | */ 576 | declare function start(decrement?: number); 577 | 578 | /** 579 | * Stop the testrunner to wait for async tests to run. Call start() to continue. 580 | * 581 | * When your async test has multiple exit points, call stop() with the increment argument, corresponding to the number of start() calls you need. 582 | * 583 | * On Blackberry 5.0, window.stop is a native read-only function. If you deal with that browser, use QUnit.stop() instead, which will work anywhere. 584 | * 585 | * @param decrement Optional argument to merge multiple stop() calls into one. Use with multiple corrsponding start() calls. 586 | */ 587 | declare function stop(increment? : number); 588 | 589 | /* CALLBACKS */ 590 | 591 | /** 592 | * Register a callback to fire whenever the test suite begins. 593 | * 594 | * QUnit.begin() is called once before running any tests. (a better would've been QUnit.start, 595 | * but thats already in use elsewhere and can't be changed.) 596 | * 597 | * @param callback Callback to execute 598 | */ 599 | declare function begin(callback: () => any); 600 | 601 | /** 602 | * Register a callback to fire whenever the test suite ends. 603 | * 604 | * @param callback Callback to execute. 605 | */ 606 | declare function done(callback: (details: DoneCallbackObject) => any); 607 | 608 | /** 609 | * Register a callback to fire whenever an assertion completes. 610 | * 611 | * This is one of several callbacks QUnit provides. Its intended for integration scenarios like 612 | * PhantomJS or Jenkins. The properties of the details argument are listed below as options. 613 | * 614 | * @param callback Callback to execute. 615 | */ 616 | declare function log(callback: (details: LogCallbackObject) => any); 617 | 618 | /** 619 | * Register a callback to fire whenever a module ends. 620 | * 621 | * @param callback Callback to execute. 622 | */ 623 | declare function moduleDone(callback: (details: ModuleDoneCallbackObject) => any); 624 | 625 | /** 626 | * Register a callback to fire whenever a module begins. 627 | * 628 | * @param callback Callback to execute. 629 | */ 630 | declare function moduleStart(callback: (name: string) => any); 631 | 632 | /** 633 | * Register a callback to fire whenever a test ends. 634 | * 635 | * @param callback Callback to execute. 636 | */ 637 | declare function testDone(callback: (details: TestDoneCallbackObject) => any); 638 | 639 | /** 640 | * Register a callback to fire whenever a test begins. 641 | * 642 | * @param callback Callback to execute. 643 | */ 644 | declare function testStart(callback: (details: TestStartCallbackObject) => any); 645 | 646 | /* TEST */ 647 | 648 | /** 649 | * Add an asynchronous test to run. The test must include a call to start(). 650 | * 651 | * For testing asynchronous code, asyncTest will automatically stop the test runner 652 | * and wait for your code to call start() to continue. 653 | * 654 | * @param name Title of unit being tested 655 | * @param expected Number of assertions in this test 656 | * @param test Function to close over assertions 657 | */ 658 | declare function asyncTest(name: string, expected?: any, test?: () => any); 659 | 660 | /** 661 | * Add an asynchronous test to run. The test must include a call to start(). 662 | * 663 | * For testing asynchronous code, asyncTest will automatically stop the test runner 664 | * and wait for your code to call start() to continue. 665 | * 666 | * @param name Title of unit being tested 667 | * @param test Function to close over assertions 668 | */ 669 | declare function asyncTest(name: string, test: () => any); 670 | 671 | /** 672 | * Specify how many assertions are expected to run within a test. 673 | * 674 | * To ensure that an explicit number of assertions are run within any test, use 675 | * expect( number ) to register an expected count. If the number of assertions 676 | * run does not match the expected count, the test will fail. 677 | * 678 | * @param amount Number of assertions in this test. 679 | */ 680 | declare function expect(amount: number); 681 | 682 | // ** conflict with TypeScript module keyword. Must be used on QUnit namespace 683 | //declare var module: (name: string, lifecycle?: LifecycleObject) => any; 684 | 685 | /** 686 | * Add a test to run. 687 | * 688 | * When testing the most common, synchronous code, use test(). 689 | * The assert argument to the callback contains all of QUnit's assertion methods. 690 | * If you are avoiding using any of QUnit's globals, you can use the assert 691 | * argument instead. 692 | * 693 | * @param title Title of unit being tested 694 | * @param expected Number of assertions in this test 695 | * @param test Function to close over assertions 696 | */ 697 | declare function test(title: string, expected: number, test: (assert?: QUnitAssert) => any); 698 | 699 | /** 700 | * @param title Title of unit being tested 701 | * @param test Function to close over assertions 702 | */ 703 | declare function test(title: string, test: (assert?: QUnitAssert) => any); 704 | 705 | declare function notPropEqual(actual: any, expected: any, message?: string); 706 | 707 | declare function propEqual(actual: any, expected: any, message?: string); 708 | 709 | // https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L1568 710 | declare function equiv(a: any, b: any); 711 | 712 | // https://github.com/jquery/qunit/blob/master/qunit/qunit.js#L661 713 | declare var raises: any; 714 | 715 | /* QUNIT */ 716 | declare var QUnit: QUnitStatic; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------