├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── index.js ├── lib └── index.js ├── mocha-support.js ├── package.json ├── smash.js ├── test ├── bind.js ├── caught.js ├── filter.js ├── generator.js ├── guard.js ├── method.js ├── mocha.opts ├── promisify.js ├── props.js ├── q_fin.js ├── q_nodify.js ├── race.js ├── reduce.js ├── tap.js ├── timers.js ├── try.js ├── utility.js ├── when_all.js ├── when_defer.js ├── when_join.js ├── when_map.js ├── when_reduce.js └── when_spread.js └── wrap.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | clean/* 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "node-style-guide", 3 | "env": { 4 | "node": true 5 | }, 6 | "globals": {}, 7 | "rules": { 8 | "space-after-keywords": "off", 9 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 10 | "object-curly-spacing": ["error", "always"], 11 | "keyword-spacing": [2, {"before": true, "after": true}], 12 | "max-statements": "off", 13 | "capitalized-comments": [ 14 | "error", 15 | "always", 16 | { 17 | "ignoreConsecutiveComments": true 18 | } 19 | ], 20 | "quotes": [ 21 | 2, 22 | "single", 23 | { 24 | "avoidEscape": true 25 | } 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | # From the nvm docs, "this installs the latest version of node". 4 | - "node" 5 | - "stable" 6 | - "9" 7 | - "8" 8 | - "7" 9 | - "6" 10 | - "5" 11 | - "4" 12 | - "4.0" 13 | - "0.12" 14 | - "0.11" 15 | - "0.10" 16 | - "0.8" 17 | before_install: 18 | - NODE_VERSION=$(node -e 'console.log(process.version.replace(/[.][0-9]+$/, ""))') 19 | - if [ "v0.8" = "$NODE_VERSION" ]; then npm install -g npm@2.7.3 ; fi 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # prfun x.x.x (not yet released) 2 | 3 | # prfun 2.1.5 (2018-01-06) 4 | * Allow functions created by `Promise.async` to accept legacy node-style 5 | callback arguments. 6 | * Switch to `eslint`, update `mocha`, and ensure we test up to node 9. 7 | 8 | # prfun 2.1.4 (2016-05-05) 9 | * Add a cache to ensure that only a single `prfun` `Promise` class is 10 | constructed even when using the wrapper interface. 11 | * Update npm dev dependencies. 12 | 13 | # prfun 2.1.3 (2015-12-11) 14 | * Optimize the `Promise` subclass constructor to avoid costly overhead 15 | in ES5 environments. The slow ES6 path is only used if necessary 16 | for correctness (or if the native Promise implementation uses ES6 17 | class syntax). 18 | * Added `Promise#then0` to the API, which is a shim when a native 19 | `Promise#then0` is not available. Some `Promise` implementations 20 | provide this method, which is much more efficient than calling 21 | `Promise#then` and discarding the result. 22 | * Used `Promise#then0` in internal implementations where appropriate, 23 | including `Promise.async` (where the use of generators can now yield 24 | better performance than chaining promises in the usual way). 25 | 26 | # prfun 2.1.2 (2015-11-20) 27 | * Ensure that `Promise.async` always returns a `Promise`. 28 | (Previously if the function returned immediately without yielding 29 | the result would not be wrapped in a promise.) 30 | 31 | # prfun 2.1.1 (2014-04-28) 32 | * Improve compatibilty with environments missing a definition of 33 | `Object.setPrototypeOf`. 34 | 35 | # prfun 2.1.0 (2015-04-27) 36 | * Switch from `es6-shim` to `core-js` by default. 37 | * Fix our subclass code to follow the latest ES6 specification. 38 | * Work around some bugs in native Promise implementations to allow 39 | `prfun` to use native promises. 40 | 41 | # prfun 2.0.0 (2015-04-23) 42 | * Breaking change: `prfun` now creates a subclass of `Promise` by 43 | default, instead of smashing the global `Promise`. This only works 44 | if your `Promise` implementation properly supports the ES6 subclass 45 | semantics -- `es6-shim` is known to implement the ES6 spec properly. 46 | To smash the global `Promise` like in the bad old days, use 47 | `require('prfun/smash')`. 48 | 49 | # prfun 1.0.2 (2014-11-06) 50 | 51 | * Fix potential resource leak in `Promise#timeout`. 52 | 53 | # prfun 1.0.1 (2014-09-25) 54 | 55 | * Added `Promise#tap`, `Promise#filter`. 56 | * Bug fix to `promisify` with named arguments (an array of names as 57 | second parameter). 58 | 59 | # prfun 1.0.0 (2014-07-15) 60 | 61 | * Breaking change to `promisify` API: following the lead of 62 | `denodeify` in `rsvp` and `q` version 2, the `promisify` function has 63 | been changed to eliminate the magic variadic argument inference. The 64 | `promisify` function now takes an explicit second parameter, `names`. 65 | If the `names` parameter is missing or falsy, then a single argument 66 | is used to resolve the promise. If the `names` parameter is `true`, 67 | then the promise is resolved with an array of the variadic arguments 68 | to the callback. If the `names` parameter is an array, the array 69 | names the variadic arguments, and the promise is resolved with an 70 | object containing fields with those names. 71 | 72 | See [comments on bower's issue #1403](https://github.com/bower/bower/pull/1403#issuecomment-48784169) 73 | and [this commit on q's v2 branch](https://github.com/kriskowal/q/commit/d5bea58bfb0fc091beb52dd91fe78506851bc7c5) 74 | for more details. 75 | 76 | # prfun 0.1.0 (2014-03-30) 77 | * Initial release. 78 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Chain over to `prfun/wrap`, which allows users to specify a non-default 2 | // Promise implementation if they prefer. 3 | module.exports = require('./wrap')(); 4 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Utility functions for ES6 Promises. 2 | 3 | var FakeMap = function FakeMap() { 4 | 'use strict'; 5 | this.entries = []; 6 | }; 7 | FakeMap.prototype._getEntry = function _getEntry(key) { 8 | 'use strict'; 9 | var entries = this.entries; 10 | for (var i = 0; i < entries.length; i++) { 11 | if (entries[i].key === key) { return entries[i]; } 12 | } 13 | }; 14 | FakeMap.prototype.set = function set(key, value) { 15 | 'use strict'; 16 | var entry = this._getEntry(key); 17 | if (entry !== void 0) { 18 | entry.value = value; 19 | } else { 20 | this.entries.push({ key: key, value: value }); 21 | } 22 | }; 23 | FakeMap.prototype.get = function get(key) { 24 | 'use strict'; 25 | var entry = this._getEntry(key); 26 | if (entry !== void 0) { 27 | return entry.value; 28 | } 29 | }; 30 | 31 | var makeMap = (function() { 32 | 'use strict'; 33 | var m = null; 34 | return function() { 35 | if (m === null) { 36 | m = global.Map ? new global.Map() : new FakeMap(); 37 | } 38 | return m; 39 | }; 40 | })(); 41 | 42 | module.exports = function(ParentPromise, smash) { 43 | 'use strict'; 44 | 45 | if (!ParentPromise) { ParentPromise = global.Promise; } 46 | // Try to use Promise implementation from core-js if there isn't already one 47 | // installed. 48 | if (!ParentPromise) { 49 | try { 50 | ParentPromise = require('core-js/library/es6/promise'); 51 | if (smash) { 52 | global.Promise = ParentPromise; 53 | } 54 | } catch (e) { 55 | throw new Error('No Promise implementation found. ' + 56 | "Install the optional dependencies to use core-js's Promises."); 57 | } 58 | } 59 | // Use cached PrFun promise if possible. 60 | var cache = makeMap(); 61 | var PrFunPromise = smash ? null : cache.get(ParentPromise); 62 | if (PrFunPromise) { return PrFunPromise; } 63 | 64 | // Find an implementation of Object.setPrototypeOf 65 | var setPrototypeOf = Object.setPrototypeOf; 66 | if (!setPrototypeOf) { 67 | try { 68 | setPrototypeOf = require('core-js/library/es6/object').setPrototypeOf; 69 | } catch (e) { 70 | throw new Error('No implementation of Object.setPrototypeOf found. ' + 71 | "Install the optional dependencies to use core-js's implementation."); 72 | } 73 | } 74 | 75 | // Create a new Promise subclass (this is less cumbersome in es6!) 76 | var makeResolve = null; 77 | PrFunPromise = (function makeSubclass(ParentPromise) { 78 | var PrFunPromise; 79 | /* eslint-disable max-len */ 80 | var isClass = function isClass(v) { 81 | // See: http://stackoverflow.com/questions/30758961/how-to-check-if-a-variable-is-an-es6-class-declaration 82 | // And: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-function.prototype.tostring 83 | return typeof v === 'function' && /^\s*class\s+/.test(v.toString()); 84 | }; 85 | /* eslint-enable max-len */ 86 | if (isClass(ParentPromise)) { 87 | // ES6 classes are currently unoptimized in V8. 88 | // So let's not use them unless ParentPromise does. 89 | try { 90 | return eval('(function(ParentPromise){' + 91 | '"use strict";' + 92 | 'return class PrFunPromise extends ParentPromise {};' + 93 | '})')(ParentPromise); 94 | } catch (e) { /* I guess true ES6 classes are not supported. */ } 95 | } 96 | // Try the "ES5 way"; this is fastest on ES5 engines. 97 | // (Faster even than ES6 classes, at least on node 5.x, but we assume 98 | // that the performance of native classes will catch up eventually.) 99 | PrFunPromise = function PrFunPromise(resolver) { 100 | ParentPromise.call(this, resolver); 101 | }; 102 | setPrototypeOf(PrFunPromise, ParentPromise); 103 | PrFunPromise.prototype = Object.create(ParentPromise.prototype); 104 | PrFunPromise.prototype.constructor = PrFunPromise; 105 | // Try it out first. 106 | try { 107 | PrFunPromise.resolve(5); 108 | // Some native promise implementations will object to the "ES5 way". 109 | // If they didn't, then let's go with this version. 110 | return PrFunPromise; 111 | } catch (e) { /* I guess we'll use the "real" ES6-compatible way. */ } 112 | PrFunPromise = function PrFunPromise(exec) { 113 | var self = new ParentPromise(exec); 114 | setPrototypeOf(self, PrFunPromise.prototype); 115 | self._promiseConstructor = PrFunPromise; 116 | return self; 117 | }; 118 | setPrototypeOf(PrFunPromise, ParentPromise); 119 | PrFunPromise.prototype = Object.create(ParentPromise.prototype); 120 | PrFunPromise.prototype.constructor = PrFunPromise; 121 | // This isn't quite right: the way we are creating the subclass 122 | // above doesn't set the internal [[PromiseConstructor]] field, 123 | // so we need to tweak the implementation of Promise.resolve() 124 | // (Note that [[PromiseConstructor]] has been removed from the 125 | // latest draft of the ES6 spec, but it may still be present in 126 | // your (buggy) native Promise implementation.) 127 | makeResolve = function(parentResolve) { 128 | return function(x) { 129 | if (x && typeof x === 'object' && x._promiseConstructor) { 130 | if (this === x._promiseConstructor) { 131 | return x; 132 | } 133 | return new this(function(r) { r(x); }); 134 | } 135 | return parentResolve.call(this, x); 136 | }; 137 | }; 138 | return PrFunPromise; 139 | })(ParentPromise); 140 | 141 | if (makeResolve) { 142 | PrFunPromise.resolve = makeResolve(PrFunPromise.resolve); 143 | } 144 | 145 | // Sometimes we just need to smash things (sigh) 146 | var Promise = smash ? ParentPromise : PrFunPromise; 147 | 148 | // Sanity-check (warn users if this is all going to go pear-shaped) 149 | try { 150 | if (!(Promise.resolve(42) instanceof Promise)) { 151 | throw new Error('Bad implementation'); 152 | } 153 | } catch (ex) { 154 | throw new Error('Bad Promise implementation: does not support ' + 155 | 'ES6 subclassing. Use prfun/smash.'); 156 | } 157 | 158 | // ---------- then0 optimization ------- 159 | 160 | // Certain promise implementations (ie, babybird) provide an implementation 161 | // of `then` which does not return a value. This can be much faster than 162 | // the native `then`. Shim it if not provided natively. 163 | if (!Promise.prototype.then0) { 164 | Promise.prototype.then0 = function(f,r) { this.then(f, r); }; 165 | } 166 | // Marker property, to indicate that we don't do anything unsafe 167 | // in our constructor. 168 | Promise.noSideEffects = true; 169 | 170 | // ---------- collections -------------- 171 | 172 | // Allow Promise.all to accept a promise for an array (or iterable). 173 | Promise.prototype.all = function() { 174 | var P = this.constructor || Promise; 175 | return this.then(function(value) { 176 | return P.all(value); 177 | }); 178 | }; 179 | 180 | // Like `Promise.all` but generate array from varargs 181 | Promise.join = function() { 182 | var P = this || Promise; 183 | // It shouldn't be necessary to slice the arguments, but some 184 | // native Promise implementations don't recognize `arguments` 185 | // as an iterable 186 | var args = new Array(arguments.length); 187 | for (var i = 0; i < args.length; ++i) { 188 | args[i] = arguments[i]; 189 | } 190 | return P.all(args); 191 | }; 192 | 193 | // Applies the `callback` to the promised value of each element of the 194 | // promised array. Returns a promise for an array of values; that is, 195 | // it implicitly applies `Promise.all` to resolve all promises returned 196 | // by the mapping function. 197 | Promise.prototype.map = function(callback, thisArg) { 198 | var P = this.constructor || Promise; 199 | return P.map(this, callback, thisArg); 200 | }; 201 | 202 | var arrayMap = Array.prototype.map; 203 | Promise.map = function(pArray, callback, thisArg) { 204 | var P = this || Promise; 205 | return P.resolve(pArray).then(function(arr) { 206 | return P.all(arrayMap.call(arr, function(pElem, index, arr_) { 207 | return P.resolve(pElem).then(function(elem) { 208 | var t = (thisArg === undefined) ? this : thisArg; 209 | return callback.call(t, elem, index, arr_); 210 | }); 211 | })); 212 | }); 213 | }; 214 | 215 | // Applies the `callback` to the promised value of each element of 216 | // the promised array, and constructs a new array of all the values 217 | // for which callback returns a (promise of a) true value. 218 | Promise.prototype.filter = function(callback, thisArg) { 219 | var P = this.constructor || Promise; 220 | return P.filter(this, callback, thisArg); 221 | }; 222 | 223 | Promise.filter = function(pArray, callback, thisArg) { 224 | var P = this || Promise; 225 | return P.resolve(pArray).then(function(arr) { 226 | var f = new Array(arr.length); 227 | return P.all(arrayMap.call(arr, function(pElem, index, arr_) { 228 | return P.resolve(pElem).then(function(elem) { 229 | var t = (thisArg === undefined) ? this : thisArg; 230 | var pBool = callback.call(t, elem, index, arr_); 231 | return P.resolve(pBool).then(function(bool) { 232 | f[index] = bool; 233 | return elem; 234 | }); 235 | }); 236 | })).then(function(resArr) { 237 | return resArr.filter(function(elem, index) { return f[index]; }); 238 | }); 239 | }); 240 | }; 241 | 242 | // Like `Promise.all` but for object properties instead of array items. 243 | Promise.prototype.props = function() { 244 | var P = this.constructor || Promise; 245 | return P.props(this); 246 | }; 247 | 248 | Promise.props = function(pObj) { 249 | var P = this || Promise; 250 | return P.resolve(pObj).then(function(obj) { 251 | var result = {}; 252 | var keys = Object.keys(obj); 253 | if (Object(obj) !== obj) { 254 | // Object.keys accepts strings, numbers, etc in ES6. 255 | throw new TypeError('not an object'); 256 | } 257 | return P.all(keys.map(function(k) { return obj[k]; })). 258 | then(function(values) { 259 | keys.forEach(function(k, i) { 260 | result[k] = values[i]; 261 | }); 262 | return result; 263 | }); 264 | }); 265 | }; 266 | 267 | // Allow Promise.race to accept a promise for an array (or iterable). 268 | Promise.prototype.race = function() { 269 | var P = this.constructor || Promise; 270 | return this.then(function(value) { 271 | return P.race(value); 272 | }); 273 | }; 274 | 275 | // Helper for reduce/reduceAll: 276 | var reducer = function(P, callback) { 277 | return function(pPrev, pCurrent, index, arr) { 278 | return P.resolve(pPrev).then(function(prev) { 279 | return P.resolve(pCurrent).then(function(current) { 280 | return callback.call(this, prev, current, index, arr); 281 | }); 282 | }); 283 | }; 284 | }; 285 | 286 | // Reduce left-to-right an array which contains promises. 287 | Promise.prototype.reduce = function(callback) { 288 | var P = this.constructor || Promise; 289 | if (arguments.length <= 1) { 290 | return P.reduce(this, callback); 291 | } 292 | return P.reduce(this, callback, arguments[1]); 293 | }; 294 | 295 | var arrayReduce = Array.prototype.reduce; 296 | Promise.reduce = function(pArray, callback) { 297 | var P = this || Promise; 298 | if (arguments.length <= 2) { 299 | return P.resolve(pArray).then(function(arr) { 300 | return arrayReduce.call(arr, reducer(P, callback)); 301 | }); 302 | } 303 | var initialValue = P.resolve(arguments[2]); 304 | return P.resolve(pArray).then(function(arr) { 305 | return arrayReduce.call(arr, reducer(P, callback), initialValue); 306 | }); 307 | }; 308 | 309 | // Reduce right-to-left an array which contains promises. 310 | Promise.prototype.reduceRight = function(callback) { 311 | var P = this.constructor || Promise; 312 | if (arguments.length <= 1) { 313 | return P.reduceRight(this, callback); 314 | } 315 | return P.reduceRight(this, callback, arguments[1]); 316 | }; 317 | 318 | var arrayReduceRight = Array.prototype.reduceRight; 319 | Promise.reduceRight = function(pArray, callback) { 320 | var P = this || Promise; 321 | if (arguments.length <= 2) { 322 | return P.resolve(pArray).then(function(arr) { 323 | return arrayReduceRight.call(arr, reducer(P, callback)); 324 | }); 325 | } 326 | var initialValue = P.resolve(arguments[2]); 327 | return P.resolve(pArray).then(function(arr) { 328 | return arrayReduceRight.call(arr, reducer(P, callback), initialValue); 329 | }); 330 | }; 331 | 332 | Promise.prototype.spread = function(onFulfilled, onRejected) { 333 | var P = this.constructor || Promise; 334 | var f = onFulfilled ? function(pArgs) { 335 | return P.all(pArgs).then(function(args) { 336 | return onFulfilled.apply(this, args); 337 | }); 338 | } : undefined; 339 | var r = onRejected ? function(pArgs) { 340 | return P.all(pArgs).then(function(args) { 341 | return onRejected.apply(this, args); 342 | }); 343 | } : undefined; 344 | return this.then(f, r); 345 | }; 346 | 347 | // ---------- Miscellaneous utility functions -------------- 348 | 349 | // Compatibility with q/when/jquery/etc. 350 | // Use of this interface is discouraged. 351 | var Deferred = function Deferred() { }; 352 | Object.defineProperties(Deferred.prototype, { 353 | resolver: { 354 | enumerable: true, 355 | get: function() { 356 | return { resolve: this.resolve, reject: this.reject }; 357 | }, 358 | }, 359 | callback: { 360 | enumerable: true, 361 | get: function() { 362 | var resolve = this.resolve; 363 | var reject = this.reject; 364 | return function(err, value) { 365 | if (err) { return reject(err); } 366 | return resolve(value); 367 | }; 368 | }, 369 | }, 370 | }); 371 | Promise.defer = function() { 372 | var P = this || Promise; 373 | var deferred = new Deferred(); 374 | deferred.promise = new P(function(resolve, reject) { 375 | deferred.resolve = resolve; 376 | deferred.reject = reject; 377 | }); 378 | return deferred; 379 | }; 380 | 381 | Promise.prototype.call = function(propertyName) { 382 | var P = this.constructor || Promise; 383 | var pArgs = new Array(arguments.length - 1); 384 | for (var i = 0; i < pArgs.length; ++i) { 385 | pArgs[i] = arguments[i + 1]; 386 | } 387 | return this.then(function(obj) { 388 | return P.all(pArgs).then(function(args) { 389 | return obj[propertyName].apply(obj, args); 390 | }); 391 | }); 392 | }; 393 | 394 | Promise.prototype.get = function(propertyName) { 395 | return this.then(function(obj) { 396 | return obj[propertyName]; 397 | }); 398 | }; 399 | 400 | Promise.prototype['return'] = function(v) { 401 | return this.then(function() { return v; }); 402 | }; 403 | 404 | Promise.prototype.tap = function(handler) { 405 | var P = this.constructor || Promise; 406 | return this.then(function(v) { 407 | return P.resolve(handler(v))['return'](v); 408 | }); 409 | }; 410 | 411 | Promise.prototype['throw'] = function(e) { 412 | // Ensure that e is resolved, if it is a promise. 413 | return this['return'](e).then(function(ee) { throw ee; }); 414 | }; 415 | 416 | Promise.prototype.done = function() { 417 | if (arguments.length > 0) { 418 | // Compatibility with other libraries which allow arguments to #done() 419 | return this.then.apply(this, arguments).done(); 420 | } 421 | this.then0(undefined, function(e) { 422 | // Throw from new scope to ensure the exception will be unhandled 423 | // (and thus reported). 424 | setTimeout(function() { throw e; }, 0); 425 | }); 426 | // This function is not chainable! Return `undefined`. 427 | }; 428 | 429 | // ---------- Timeouts and delays -------------- 430 | 431 | Promise.prototype.delay = function(ms) { 432 | var P = this.constructor || Promise; 433 | return P.delay(this, ms); 434 | }; 435 | 436 | Promise.delay = function(pValue, ms) { 437 | var P = this || Promise; 438 | if (arguments.length === 1) { 439 | ms = pValue; 440 | pValue = undefined; 441 | } 442 | return P.resolve(pValue).then(function(value) { 443 | return new P(function(resolve, reject) { // eslint-disable-line 444 | setTimeout(function() { resolve(value); }, ms); 445 | }); 446 | }); 447 | }; 448 | 449 | var TimeoutError = Promise.TimeoutError = function(message) { 450 | this.message = (typeof message === 'string') ? message : 'timeout'; 451 | this.name = 'TimeoutError'; 452 | if (Error.captureStackTrace) { 453 | Error.captureStackTrace(this, this.constructor); 454 | } 455 | }; 456 | TimeoutError.prototype = Object.create(Error.prototype); 457 | 458 | var makeRejector = function(reject, message, ms) { 459 | // Create this function in an outer scope so that we don't inadvertently 460 | // keep a reference to the promise here. Perhaps this is overkill. 461 | var id = setTimeout(function() { reject(new TimeoutError(message)); }, ms); 462 | return function() { clearTimeout(id); }; 463 | }; 464 | Promise.prototype.timeout = function(ms, message) { 465 | var P = this.constructor || Promise; 466 | var promise = this; 467 | return new P(function(resolve, reject) { 468 | promise.then0(resolve, reject); 469 | var cleanup = makeRejector(reject, message, ms); 470 | promise.then0(cleanup, cleanup); 471 | }); 472 | }; 473 | 474 | // ---------- try/caught/finally -------------- 475 | 476 | Promise['try'] = function(fn, ctx /* ..args */) { // eslint-disable-line 477 | var P = this || Promise; 478 | // It shouldn't be necessary to slice the arguments, but some 479 | // native Promise implementations don't recognize `arguments` 480 | // as an iterable. 481 | var args = new Array(arguments.length); 482 | for (var i = 0; i < args.length; ++i) { 483 | args[i] = arguments[i]; 484 | } 485 | return P.all(args).then(function(args) { 486 | var fn = args[0]; 487 | var ctx = args[1]; 488 | var rest = args.slice(2); 489 | return new P(function(resolve, reject) { 490 | try { 491 | resolve(fn.apply(ctx, rest)); 492 | } catch (e) { 493 | reject(e); 494 | } 495 | }); 496 | }); 497 | }; 498 | 499 | Promise.prototype.caught = function(predicate, handler) { 500 | var promise = this; 501 | if (arguments.length <= 1) { return promise['catch'](predicate); } 502 | var predicates = new Array(arguments.length); 503 | for (var i = 0; i < predicates.length; ++i) { 504 | predicates[i] = arguments[i]; 505 | } 506 | handler = predicates.pop(); 507 | predicates = predicates.map(function(v) { 508 | var isErrorType = (v === Error) || 509 | (v != null && v.prototype instanceof Error); 510 | if (isErrorType) { 511 | return function(e) { return (e instanceof v); }; 512 | } 513 | if (typeof v === 'function') { 514 | return function(e) { return !!v(e); }; 515 | } 516 | return function(e) { // eslint-disable-line 517 | throw new TypeError('caught filter must inherit from Error ' + 518 | 'or be a simple predicate function'); 519 | }; 520 | }); 521 | return promise['catch'](function(e) { 522 | for (var i = 0; i < predicates.length; i++) { 523 | if (predicates[i](e)) { 524 | return handler.call(this, e); 525 | } 526 | } 527 | // Re-throw 528 | throw e; 529 | }); 530 | }; 531 | 532 | // XXX The return semantics are a bit funny. 533 | // eg: 534 | // > f = function(g, h) { try { return g(); } finally { return h(); } }; 535 | // > f(function(){return 1;}, function(){return 2;}) 536 | // 2 537 | // > f(function(){return 1;}, function(){throw new Error('b');}) 538 | // Error: b 539 | // This method would return '1' in the first case (but 'b' in the second). 540 | Promise.prototype['finally'] = function(handler) { 541 | var promise = this; 542 | var P = this.constructor || Promise; 543 | return new P(function(resolve, reject) { 544 | promise.then0(function(value) { 545 | var cb = function() { resolve(value); }; 546 | P.resolve().then(handler).then0(cb, reject); 547 | }, function(reason) { 548 | var cb = function() { reject(reason); }; 549 | P.resolve().then(handler).then0(cb, reject); 550 | }); 551 | }); 552 | }; 553 | 554 | // ---------- wrappers and function-writing helpers -------------- 555 | 556 | // Transparently handle synchronous exceptions and early returns. 557 | // This is like `Q.promised`. 558 | Promise.method = function(fn) { 559 | var P = this || Promise; 560 | if (typeof fn !== 'function') { 561 | throw new TypeError('must wrap a function'); // Fail fast 562 | } 563 | return function() { 564 | var self = this; 565 | var args = new Array(arguments.length); 566 | for (var i = 0; i < args.length; ++i) { 567 | args[i] = arguments[i]; 568 | } 569 | return P.resolve(self).then(function(self) { 570 | return P.all(args).then(function(args) { 571 | return new P(function(resolve, reject) { 572 | try { 573 | resolve(fn.apply(self, args)); 574 | } catch (e) { 575 | reject(e); 576 | } 577 | }); 578 | }); 579 | }); 580 | }; 581 | }; 582 | 583 | // Register a node-style callback on this promise. 584 | Promise.prototype.nodify = function(callback) { 585 | var promise = this; 586 | if (callback) { 587 | var queueThrow = function(e) { 588 | // Don't let this exception get swallowed by the Promise handlers 589 | setTimeout(function() { throw e; }, 0); 590 | }; 591 | promise.then0(function(v) { 592 | try { 593 | callback.call(this, null, v); 594 | } catch (e) { 595 | queueThrow(e); 596 | } 597 | }, function(r) { 598 | try { 599 | callback.call(this, r); 600 | } catch (e) { 601 | queueThrow(e); 602 | } 603 | }); 604 | } 605 | return promise; 606 | }; 607 | 608 | // Returns a function that wraps a given nodeFunction 609 | Promise.promisify = function(nodeFunction, names, optThis) { 610 | var P = this || Promise; 611 | var hasThis = (arguments.length > 2); 612 | return function(a, b, c) { 613 | var self = hasThis ? optThis : this; 614 | var l = arguments.length; 615 | if (l <= 3 && !names) { 616 | // This section is a bit redundant, but it improves performance 617 | // in the common case. 618 | return new P(function(resolve, reject) { 619 | var cb = function(e, v) { 620 | if (e) { 621 | reject(e); 622 | } else { 623 | resolve(v); 624 | } 625 | }; 626 | switch (l) { 627 | case 0: { nodeFunction.call(self, cb); return; } 628 | case 1: { nodeFunction.call(self, a, cb); return; } 629 | case 2: { nodeFunction.call(self, a, b, cb); return; } 630 | case 3: { nodeFunction.call(self, a, b, c, cb); return; } 631 | default: { throw new Error('unreachable'); } 632 | } 633 | }); 634 | } 635 | var args = new Array(l + 1); 636 | for (var i = 0; i < l; ++i) { 637 | args[i] = arguments[i]; 638 | } 639 | return new P(function(resolve, reject) { 640 | args[l] = function(e, v) { 641 | if (e) { 642 | reject(e); 643 | } else if (names === true) { 644 | var a = new Array(arguments.length - 1); 645 | for (var j = 0; j < a.length; ++j) { 646 | a[j] = arguments[j + 1]; 647 | } 648 | resolve(a); 649 | } else if (names) { 650 | var value = {}; 651 | for (var index in names) { 652 | value[names[index]] = arguments[(+index) + 1]; 653 | } 654 | resolve(value); 655 | } else { 656 | resolve(v); 657 | } 658 | }; 659 | nodeFunction.apply(self, args); 660 | }); 661 | }; 662 | }; 663 | 664 | // Guards: limit the amount of parallelism. 665 | 666 | // Implementation borrowed from: 667 | // https://github.com/cujojs/when/blob/master/guard.js 668 | // Authors: Brian Cavalier, John Hann, Sakari Jokinen 669 | // docs at: https://github.com/cujojs/when/blob/master/docs/api.md#whenguard 670 | 671 | /** 672 | * Creates a guarded version of f that can only be entered when the supplied 673 | * condition allows. 674 | * @param {function} condition represents a critical section that may only 675 | * be entered when allowed by the condition 676 | * @param {function} f function to guard 677 | * @returns {function} guarded version of f 678 | */ 679 | Promise.guard = function(condition, fn) { 680 | var P = this || Promise; 681 | if (typeof condition === 'number') { 682 | condition = P.guard.n(condition); 683 | } 684 | return function() { 685 | var self, args; 686 | 687 | self = this; 688 | args = new Array(arguments.length); 689 | for (var i = 0; i < args.length; i++) { 690 | args[i] = arguments[i]; 691 | } 692 | 693 | return P.resolve(condition()).then(function(exit) { 694 | return P.resolve(fn.apply(self, args)).finally(exit); 695 | }); 696 | }; 697 | }; 698 | 699 | /** 700 | * Creates a condition that allows only n simultaneous executions 701 | * of a guarded function 702 | * @param {number} allowed number of allowed simultaneous executions 703 | * @returns {function} condition function which returns a promise that 704 | * fulfills when the critical section may be entered. The fulfillment 705 | * value is a function ("notifyExit") that must be called when the critical 706 | * section has been exited. 707 | */ 708 | Promise.guard.n = function(allowed) { 709 | var count, waiting; 710 | 711 | count = 0; 712 | waiting = []; 713 | 714 | var exit = function() { 715 | if (count > 0) { 716 | count--; 717 | } 718 | if (waiting.length) { 719 | waiting.shift()(exit); 720 | } 721 | }; 722 | 723 | return function enter() { 724 | return new Promise(function(resolve) { 725 | if (count < allowed) { 726 | resolve(exit); 727 | } else { 728 | waiting.push(resolve); 729 | } 730 | count += 1; 731 | }); 732 | }; 733 | }; 734 | 735 | // Promise#bind() 736 | // Idea borrowed from bluebird. 737 | Promise.bind = function(newThis) { 738 | return this.resolve().bind(newThis); 739 | }; 740 | 741 | Promise.prototype.bind = function(newThis) { 742 | var SuperPromise = this._bindSuper || this.constructor || Promise; 743 | // Create a new Promise subclass (this is less cumbersome in es6, sigh) 744 | var BoundPromise = function BoundPromise(exec) { 745 | var self = new SuperPromise(exec); 746 | setPrototypeOf(self, BoundPromise.prototype); 747 | self._promiseConstructor = BoundPromise; 748 | return self; 749 | }; 750 | setPrototypeOf(BoundPromise, SuperPromise); 751 | BoundPromise.prototype = Object.create(SuperPromise.prototype); 752 | BoundPromise.prototype.constructor = BoundPromise; 753 | BoundPromise.prototype._bindSuper = SuperPromise; 754 | 755 | // This re-definition of 'then' is where the actual work happens. 756 | BoundPromise.prototype.then = (function(superThen) { 757 | return function(f, r) { 758 | var ff = f && f.bind(newThis); 759 | var rr = r && r.bind(newThis); 760 | return superThen.call(this, ff, rr); 761 | }; 762 | })(BoundPromise.prototype.then); 763 | BoundPromise.prototype.then0 = function(f,r) { this.then(f, r); }; 764 | 765 | // See discussion of PrFunPromise.resolve above: 766 | if (makeResolve) { 767 | BoundPromise.resolve = makeResolve(BoundPromise.resolve); 768 | } 769 | 770 | return newThis ? BoundPromise.resolve(this) : SuperPromise.resolve(this); 771 | }; 772 | 773 | // Generators. 774 | Promise.async = function(makeGenerator, cbArg) { 775 | var P = this || Promise; 776 | return function() { 777 | var generator = makeGenerator.apply(this, arguments); 778 | // Isolate try/catch to standalone functions, since v8 779 | // will not optimize any method containing a `try` block. 780 | var errObject = { e: null }; 781 | var tryCatchNext = function(arg) { 782 | try { 783 | return generator.next(arg); 784 | } catch (e) { 785 | errObject.e = e; 786 | return errObject; 787 | } 788 | }; 789 | var tryCatchThrow = function(arg) { 790 | try { 791 | return generator['throw'](arg); 792 | } catch (e) { 793 | errObject.e = e; 794 | return errObject; 795 | } 796 | }; 797 | var resultP = new P(function(resolve, reject) { 798 | var callback, errback; 799 | var continuer = function(fn, arg) { 800 | var result = fn(arg); 801 | if (result === errObject) { 802 | reject(result.e); 803 | return; 804 | } 805 | if (result.done) { 806 | resolve(result.value); 807 | return; 808 | } 809 | // Using then0 here yields a significant performance improvement. 810 | P.resolve(result.value).then0(callback, errback); 811 | }; 812 | callback = function(arg) { return continuer(tryCatchNext, arg); }; 813 | errback = function(e) { return continuer(tryCatchThrow, e); }; 814 | callback(); 815 | }); 816 | return (cbArg === undefined) ? resultP : 817 | Promise.prototype.nodify.call(resultP, arguments[cbArg]); 818 | }; 819 | }; 820 | 821 | if (!smash) { cache.set(ParentPromise, Promise); } 822 | return Promise; 823 | }; 824 | -------------------------------------------------------------------------------- /mocha-support.js: -------------------------------------------------------------------------------- 1 | // Extra support for mocha. 2 | // 3 | // The new "return a Promise" support in mocha is very nice, but it opens 4 | // up a new way to screw up your tests: since it uses the synchronous 5 | // test style, if you intend to return a Promise but forget to, the Promise 6 | // will be orphaned and the test will complete regardless of whether the 7 | // Promise had been rejected or not. Whoops: now your test is effectively 8 | // being silently skipped. 9 | // 10 | // Since the vast majority of our "synchronous" tests in this package actually 11 | // involve Promises, do a little bit of magic redefinition of the `it` and 12 | // `specify` functions to assert that they *always* return a `Promise`. 13 | // This ensures we can't accidentally write no-op tests. 14 | 15 | var assert = require('assert'); 16 | // Always use core-js promises for tests, since platform Promises may 17 | // not properly implement ES6 subclasses. 18 | delete global.Promise; 19 | // Loading promises as a library was broken in core-js 0.9.0; it was fixed 20 | // by zloirock/core-js@bba6d8d included in 0.9.1. Previously we needed to 21 | // load all of core-js here to work around the issue. 22 | // require('core-js'); 23 | 24 | // Wrap specify/it and ensure that they always return a promise if synchronous. 25 | var onlypromises = function(f) { 26 | return function(title, cb) { 27 | var cb2; 28 | if (cb.length === 1) { 29 | cb2 = function(done) { 30 | assert(arguments.length === 1); 31 | var result = cb(done); 32 | assert.ok(!result, 33 | 'Tests with callbacks should not return a value: ' + title); 34 | }; 35 | } else if (cb.length === 0) { 36 | cb2 = function() { 37 | assert(arguments.length === 0); 38 | var result = cb(); 39 | assert.ok(result && typeof result.then === 'function', 40 | 'Synchronous tests should return a promise: ' + title); 41 | return result; 42 | }; 43 | } else { 44 | assert(false); 45 | } 46 | return f(title, cb2); 47 | }; 48 | }; 49 | 50 | // This module actually is loaded before mocha defines the specify/it 51 | // functions, so define a getter/setter on global that will intercept 52 | // the future definitions of specify/it and wrap them. 53 | var realSpecify = onlypromises(global.specify); 54 | Object.defineProperty(global, 'specify', { 55 | get: function() { return realSpecify; }, 56 | set: function(v) { realSpecify = onlypromises(v); }, 57 | }); 58 | 59 | var realIt = onlypromises(global.it); 60 | Object.defineProperty(global, 'it', { 61 | get: function() { return realIt; }, 62 | set: function(v) { realIt = onlypromises(v); }, 63 | }); 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prfun", 3 | "version": "2.1.5+git", 4 | "description": "Helper functions for ES6 promises", 5 | "main": "index.js", 6 | "scripts": { 7 | "eslint": "eslint .", 8 | "eslint-fix": "eslint --fix .", 9 | "lint": "npm run eslint", 10 | "lint-no-0.x": "node -e 'process.exit(/v0[.][0-9]+[.]/.test(process.version) ? 0 : 1)' || npm run lint", 11 | "mocha": "if node -e 'process.exit(/v0[.]([0-9]|10)[.]/.test(process.version) ? 0 : 1)' ; then mocha ; else mocha --harmony ; fi", 12 | "test": "npm run lint-no-0.x && npm run mocha" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/cscott/prfun" 17 | }, 18 | "keywords": [ 19 | "promise", 20 | "promises", 21 | "es6", 22 | "harmony" 23 | ], 24 | "author": "C. Scott Ananian", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/cscott/prfun/issues" 28 | }, 29 | "homepage": "https://github.com/cscott/prfun", 30 | "devDependencies": { 31 | "core-js": "^2.5.3", 32 | "eslint": "^4.14.0", 33 | "eslint-config-node-style-guide": "^3.0.0", 34 | "mocha": "^3.5.0" 35 | }, 36 | "optionalDependencies": { 37 | "core-js": "^2.5.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /smash.js: -------------------------------------------------------------------------------- 1 | /* Not recommended! Mutate the global Promise. */ 2 | require('./lib')(global.Promise, 'HULK SMASH'); 3 | -------------------------------------------------------------------------------- /test/bind.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | var fulfilled = Promise.resolve.bind(Promise); 8 | var rejected = Promise.reject.bind(Promise); 9 | var pending = Promise.defer.bind(Promise); 10 | 11 | var THIS = { name: 'this' }; 12 | 13 | function CustomError1() {} 14 | CustomError1.prototype = Object.create(Error.prototype); 15 | function CustomError2() {} 16 | CustomError2.prototype = Object.create(Error.prototype); 17 | 18 | 19 | describe('when using .bind', function() { 20 | describe('with finally', function() { 21 | describe('this should refer to the bound object', function() { 22 | specify('in straight-forward handler', function() { 23 | return fulfilled().bind(THIS)['finally'](function() { 24 | assert(this === THIS); 25 | }); 26 | }); 27 | 28 | specify('after promise returned from finally resolves', function() { 29 | var d = pending(); 30 | var promise = d.promise; 31 | var waited = false; 32 | 33 | setTimeout(function() { 34 | waited = true; 35 | d.resolve(); 36 | }, 50); 37 | 38 | return fulfilled().bind(THIS)['finally'](function() { 39 | return promise; 40 | })['finally'](function() { 41 | assert(waited); 42 | assert(this === THIS); 43 | }); 44 | }); 45 | }); 46 | }); 47 | 48 | 49 | describe('with .delay', function() { 50 | describe('this should refer to the bound object', function() { 51 | specify('in straight-forward handler', function() { 52 | return fulfilled(3).bind(THIS).delay(5).then(function(v) { 53 | assert(v === 3); 54 | assert(this === THIS); 55 | }); 56 | }); 57 | specify('in rejected handler', function() { 58 | return rejected(3).bind(THIS).delay(5).then(assert.fail, function(v) { 59 | assert(v === 3); 60 | assert(this === THIS); 61 | }); 62 | }); 63 | }); 64 | }); 65 | 66 | 67 | describe('with .timeout', function() { 68 | describe('this should refer to the bound object', function() { 69 | specify('in straight-forward handler', function() { 70 | return fulfilled(3).bind(THIS).timeout(500).then(function(v) { 71 | assert(v === 3); 72 | assert(this === THIS); 73 | }); 74 | }); 75 | specify('in rejected handler', function() { 76 | return rejected(3).bind(THIS).timeout(500).then(assert.fail, function(v) { 77 | assert(v === 3); 78 | assert(this === THIS); 79 | }); 80 | }); 81 | 82 | specify('in rejected handler after timeout', function() { 83 | return new Promise(function() {}) 84 | .bind(THIS).timeout(10).then(assert.fail) 85 | .caught(Promise.TimeoutError, function(err) { // eslint-disable-line no-unused-vars 86 | assert(this === THIS); 87 | }); 88 | }); 89 | }); 90 | 91 | }); 92 | 93 | 94 | describe('With #caught', function() { 95 | describe('this should refer to the bound object', function() { 96 | specify('in an immediately trapped catch handler', function() { 97 | return fulfilled().bind(THIS).then(function() { 98 | assert(THIS === this); 99 | var a; 100 | a.b(); 101 | }).caught(Error, function(e) { // eslint-disable-line no-unused-vars 102 | assert(THIS === this); 103 | }); 104 | }); 105 | specify('in a later trapped catch handler', function() { 106 | return fulfilled().bind(THIS).then(function() { 107 | throw new CustomError1(); 108 | }).caught(CustomError2, assert.fail) 109 | .caught(CustomError1, function(e) { // eslint-disable-line no-unused-vars 110 | assert(THIS === this); 111 | }); 112 | }); 113 | }); 114 | }); 115 | 116 | 117 | describe('With .try promises', function() { 118 | specify('this should refer to the bound object', function() { 119 | return Promise.bind(THIS).constructor.try(function() { 120 | assert(this !== THIS); 121 | return 42; 122 | }).then(function(v) { 123 | assert(v === 42); 124 | assert(this === THIS); 125 | }); 126 | }); 127 | specify('explicit context should override', function() { 128 | var NOTTHIS = {}; 129 | return Promise.bind(THIS).constructor.try(function() { 130 | assert(this === NOTTHIS); 131 | return 42; 132 | }, NOTTHIS).then(function(v) { 133 | assert(v === 42); 134 | assert(this === THIS); 135 | }); 136 | }); 137 | }); 138 | 139 | 140 | describe('With .method promises', function() { 141 | specify('this should refer to the bound object', function() { 142 | var f = Promise.bind(THIS).constructor.method(function() { 143 | assert(this !== THIS); 144 | return 42; 145 | }); 146 | return f().then(function(v) { 147 | assert(v === 42); 148 | assert(this === THIS); 149 | }); 150 | }); 151 | specify('explicit context should override', function() { 152 | var NOTTHIS = {}; 153 | var f = Promise.bind(THIS).constructor.method(function() { 154 | assert(this === NOTTHIS); 155 | return 42; 156 | }); 157 | return f.call(NOTTHIS).then(function(v) { 158 | assert(v === 42); 159 | assert(this === THIS); 160 | }); 161 | }); 162 | }); 163 | 164 | 165 | describe('With .guard promises', function() { 166 | specify('this should refer to the bound object', function() { 167 | var f = Promise.bind(THIS).constructor.guard(1, function() { 168 | assert(this !== THIS); 169 | return 42; 170 | }); 171 | return f().then(function(v) { 172 | assert(v === 42); 173 | assert(this === THIS); 174 | }); 175 | }); 176 | specify('explicit context should override', function() { 177 | var NOTTHIS = {}; 178 | var f = Promise.bind(THIS).constructor.guard(1, function() { 179 | assert(this === NOTTHIS); 180 | return 42; 181 | }); 182 | return f.call(NOTTHIS).then(function(v) { 183 | assert(v === 42); 184 | assert(this === THIS); 185 | }); 186 | }); 187 | }); 188 | 189 | 190 | describe('With #get promises', function() { 191 | specify('this should refer to the bound object', function() { 192 | return fulfilled({ key: 'value' }).bind(THIS).get('key').then(function(val) { 193 | assert(val === 'value'); 194 | assert(this === THIS); 195 | }); 196 | }); 197 | }); 198 | 199 | 200 | describe('With #call promises', function() { 201 | specify('this should refer to the bound object', function() { 202 | return fulfilled({ key: function() {return 'value';} }).bind(THIS).call('key').then(function(val) { 203 | assert(val === 'value'); 204 | assert(this === THIS); 205 | }); 206 | }); 207 | }); 208 | 209 | 210 | describe('With #return promises', function() { 211 | specify('this should refer to the bound object', function() { 212 | return fulfilled().bind(THIS).return('value').then(function(val) { 213 | assert(val === 'value'); 214 | assert(this === THIS); 215 | }); 216 | }); 217 | }); 218 | 219 | 220 | describe('With #throw promises', function() { 221 | specify('this should refer to the bound object', function() { 222 | return fulfilled().bind(THIS).throw('value').then(assert.fail, function(val) { 223 | assert(val === 'value'); 224 | assert(this === THIS); 225 | }); 226 | }); 227 | }); 228 | 229 | 230 | describe('With #spread promises', function() { 231 | 232 | describe('this should refer to the bound object', function() { 233 | specify('when spreading immediate array', function() { 234 | return fulfilled([1, 2, 3]).bind(THIS).spread(function(a, b, c) { 235 | assert(c === 3); 236 | assert(this === THIS); 237 | }); 238 | }); 239 | specify('when spreading eventual array', function() { 240 | var d = pending(); 241 | var promise = d.promise; 242 | setTimeout(function() { 243 | d.resolve([1, 2, 3]); 244 | }, 50); 245 | return promise.bind(THIS).spread(function(a, b, c) { 246 | assert(c === 3); 247 | assert(this === THIS); 248 | }); 249 | }); 250 | 251 | specify('when spreading eventual array of eventual values', function() { 252 | var d = pending(); 253 | var promise = d.promise; 254 | setTimeout(function() { 255 | var d1 = pending(); 256 | var p1 = d1.promise; 257 | 258 | var d2 = pending(); 259 | var p2 = d2.promise; 260 | 261 | var d3 = pending(); 262 | var p3 = d3.promise; 263 | d.resolve([p1, p2, p3]); 264 | 265 | setTimeout(function() { 266 | d1.resolve(1); 267 | d2.resolve(2); 268 | d3.resolve(3); 269 | }, 3); 270 | }, 50); 271 | return promise.bind(THIS).spread(function(a, b, c) { 272 | assert(c === 3); 273 | assert(this === THIS); 274 | }); 275 | }); 276 | }); 277 | }); 278 | 279 | 280 | describe('With .promisify', function() { 281 | describe('this should refer to the bound object', function() { 282 | specify('on success', function() { 283 | var obj = { foo: function(cb) { cb(null, this.bar); }, bar: 42 }; 284 | var foo = Promise.bind(THIS).constructor.promisify(obj.foo, false, obj); 285 | return foo().then(function(v) { 286 | assert(v === 42); 287 | assert(this === THIS); 288 | }); 289 | }); 290 | specify('on failure', function() { 291 | var obj = { foo: function(cb) { cb(this.bar); }, bar: 42 }; 292 | var foo = Promise.bind(THIS).constructor.promisify(obj.foo, false, obj); 293 | return foo().then(assert.fail, function(v) { 294 | assert(v === 42); 295 | assert(this === THIS); 296 | }); 297 | }); 298 | }); 299 | }); 300 | 301 | describe('With #nodify', function() { 302 | describe('this should refer to the bound object', function() { 303 | specify('when the callback succeeeds', function(done) { 304 | fulfilled(3).bind(THIS).nodify(function(err, success) { 305 | try { 306 | assert(err === null); 307 | assert(success === 3); 308 | assert(this === THIS); 309 | done(); 310 | } catch (e) { 311 | done(e); 312 | } 313 | }); 314 | }); 315 | specify('when the callback errs', function(done) { 316 | rejected(3).bind(THIS).nodify(function(err, success) { // eslint-disable-line no-unused-vars 317 | try { 318 | assert(err === 3); 319 | assert(this === THIS); 320 | done(); 321 | } catch (e) { 322 | done(e); 323 | } 324 | }); 325 | }); 326 | }); 327 | }); 328 | 329 | 330 | describe('With #map', function() { 331 | describe('this should refer to the bound object', function() { 332 | specify('inside the mapper with immediate values', function() { 333 | return fulfilled([1, 2, 3]).bind(THIS).map(function(v, i) { 334 | if (i === 2) { 335 | assert(this === THIS); 336 | } 337 | }); 338 | }); 339 | specify('inside the mapper with eventual values', function() { 340 | var d1 = pending(); 341 | var p1 = d1.promise; 342 | 343 | var d2 = pending(); 344 | var p2 = d2.promise; 345 | 346 | var d3 = pending(); 347 | var p3 = d3.promise; 348 | 349 | setTimeout(function() { 350 | d1.resolve(1); 351 | d2.resolve(2); 352 | d3.resolve(3); 353 | }, 50); 354 | 355 | return fulfilled([p1, p2, p3]).bind(THIS).map(function(v, i) { 356 | if (i === 2) { 357 | assert(this === THIS); 358 | } 359 | }); 360 | }); 361 | 362 | specify('after the mapper with immediate values', function() { 363 | return fulfilled([1, 2, 3]).bind(THIS).map(function() { 364 | return 1; 365 | }).then(function() { 366 | assert(this === THIS); 367 | }); 368 | }); 369 | 370 | specify('after the mapper with eventual values', function() { 371 | var d1 = pending(); 372 | var p1 = d1.promise; 373 | 374 | var d2 = pending(); 375 | var p2 = d2.promise; 376 | 377 | var d3 = pending(); 378 | var p3 = d3.promise; 379 | 380 | setTimeout(function() { 381 | d1.resolve(1); 382 | d2.resolve(2); 383 | d3.resolve(3); 384 | }, 50); 385 | 386 | return fulfilled([p1, p2, p3]).bind(THIS).map(function() { 387 | return 1; 388 | }).then(function() { 389 | assert(this === THIS); 390 | }); 391 | }); 392 | 393 | specify('after the mapper with immediate values when the map returns promises', function() { 394 | var d1 = pending(); 395 | var p1 = d1.promise; 396 | 397 | setTimeout(function() { 398 | d1.resolve(1); 399 | }, 50); 400 | 401 | return fulfilled([1, 2, 3]).bind(THIS).map(function() { 402 | return p1; 403 | }).then(function() { 404 | assert(this === THIS); 405 | }); 406 | }); 407 | }); 408 | 409 | describe('this should not refer to the bound object', function() { 410 | specify('in the promises created within the handler', function() { 411 | var d1 = pending(); 412 | var p1 = d1.promise; 413 | 414 | setTimeout(function() { 415 | d1.resolve(1); 416 | }, 50); 417 | 418 | return fulfilled([1, 2, 3]).bind(THIS).map(function() { 419 | return p1.then(function() { 420 | assert(this !== THIS); 421 | return 1; 422 | }); 423 | }).then(function() { 424 | assert(this === THIS); 425 | }); 426 | }); 427 | }); 428 | }); 429 | 430 | describe('With #reduce', function() { 431 | describe('this should refer to the bound object', function() { 432 | specify('inside the reducer with immediate values', function() { 433 | return fulfilled([1, 2, 3]).bind(THIS).reduce(function(prev, v, i) { 434 | if (i === 2) { 435 | assert(this === THIS); 436 | } 437 | }); 438 | }); 439 | specify('inside the reducer with eventual values', function() { 440 | var d1 = pending(); 441 | var p1 = d1.promise; 442 | 443 | var d2 = pending(); 444 | var p2 = d2.promise; 445 | 446 | var d3 = pending(); 447 | var p3 = d3.promise; 448 | 449 | setTimeout(function() { 450 | d1.resolve(1); 451 | d2.resolve(2); 452 | d3.resolve(3); 453 | }, 50); 454 | 455 | return fulfilled([p1, p2, p3]).bind(THIS).reduce(function(prev, v, i) { 456 | if (i === 2) { 457 | assert(this === THIS); 458 | } 459 | }); 460 | }); 461 | 462 | specify('after the reducer with immediate values', function() { 463 | return fulfilled([1, 2, 3]).bind(THIS).reduce(function() { 464 | return 1; 465 | }).then(function() { 466 | assert(this === THIS); 467 | }); 468 | }); 469 | 470 | specify('after the reducer with eventual values', function() { 471 | var d1 = pending(); 472 | var p1 = d1.promise; 473 | 474 | var d2 = pending(); 475 | var p2 = d2.promise; 476 | 477 | var d3 = pending(); 478 | var p3 = d3.promise; 479 | 480 | setTimeout(function() { 481 | d1.resolve(1); 482 | d2.resolve(2); 483 | d3.resolve(3); 484 | }, 50); 485 | 486 | return fulfilled([p1, p2, p3]).bind(THIS).reduce(function() { 487 | return 1; 488 | }).then(function() { 489 | assert(this === THIS); 490 | }); 491 | }); 492 | 493 | specify('after the reducer with immediate values when the reducer returns promise', function() { 494 | var d1 = pending(); 495 | var p1 = d1.promise; 496 | 497 | setTimeout(function() { 498 | d1.resolve(1); 499 | }, 50); 500 | 501 | return fulfilled([1, 2, 3]).bind(THIS).reduce(function() { 502 | return p1; 503 | }).then(function() { 504 | assert(this === THIS); 505 | }); 506 | }); 507 | }); 508 | 509 | describe('this should not refer to the bound object', function() { 510 | specify('in the promises created within the handler', function() { 511 | var d1 = pending(); 512 | var p1 = d1.promise; 513 | 514 | setTimeout(function() { 515 | d1.resolve(1); 516 | }, 50); 517 | 518 | return fulfilled([1, 2, 3]).bind(THIS).reduce(function() { 519 | return p1.then(function() { 520 | assert(this !== THIS); 521 | return 1; 522 | }); 523 | }).then(function() { 524 | assert(this === THIS); 525 | }); 526 | }); 527 | }); 528 | }); 529 | 530 | 531 | describe('With #reduceRight', function() { 532 | describe('this should refer to the bound object', function() { 533 | specify('inside the reducer with immediate values', function() { 534 | return fulfilled([1, 2, 3]).bind(THIS).reduceRight(function(prev, v, i) { 535 | if (i === 2) { 536 | assert(this === THIS); 537 | } 538 | }); 539 | }); 540 | specify('inside the reducer with eventual values', function() { 541 | var d1 = pending(); 542 | var p1 = d1.promise; 543 | 544 | var d2 = pending(); 545 | var p2 = d2.promise; 546 | 547 | var d3 = pending(); 548 | var p3 = d3.promise; 549 | 550 | setTimeout(function() { 551 | d1.resolve(1); 552 | d2.resolve(2); 553 | d3.resolve(3); 554 | }, 50); 555 | 556 | return fulfilled([p1, p2, p3]).bind(THIS). 557 | reduceRight(function(prev, v, i) { 558 | if (i === 2) { 559 | assert(this === THIS); 560 | } 561 | }); 562 | }); 563 | 564 | specify('after the reducer with immediate values', function() { 565 | return fulfilled([1, 2, 3]).bind(THIS).reduceRight(function() { 566 | return 1; 567 | }).then(function() { 568 | assert(this === THIS); 569 | }); 570 | }); 571 | 572 | specify('after the reducer with eventual values', function() { 573 | var d1 = pending(); 574 | var p1 = d1.promise; 575 | 576 | var d2 = pending(); 577 | var p2 = d2.promise; 578 | 579 | var d3 = pending(); 580 | var p3 = d3.promise; 581 | 582 | setTimeout(function() { 583 | d1.resolve(1); 584 | d2.resolve(2); 585 | d3.resolve(3); 586 | }, 50); 587 | 588 | return fulfilled([p1, p2, p3]).bind(THIS).reduceRight(function() { 589 | return 1; 590 | }).then(function() { 591 | assert(this === THIS); 592 | }); 593 | }); 594 | 595 | specify('after the reducer with immediate values when the reducer returns promise', function() { 596 | var d1 = pending(); 597 | var p1 = d1.promise; 598 | 599 | setTimeout(function() { 600 | d1.resolve(1); 601 | }, 50); 602 | 603 | return fulfilled([1, 2, 3]).bind(THIS).reduceRight(function() { 604 | return p1; 605 | }).then(function() { 606 | assert(this === THIS); 607 | }); 608 | }); 609 | }); 610 | 611 | describe('this should not refer to the bound object', function() { 612 | specify('in the promises created within the handler', function() { 613 | var d1 = pending(); 614 | var p1 = d1.promise; 615 | 616 | setTimeout(function() { 617 | d1.resolve(1); 618 | }, 50); 619 | 620 | return fulfilled([1, 2, 3]).bind(THIS).reduceRight(function() { 621 | return p1.then(function() { 622 | assert(this !== THIS); 623 | return 1; 624 | }); 625 | }).then(function() { 626 | assert(this === THIS); 627 | }); 628 | }); 629 | }); 630 | }); 631 | 632 | 633 | describe('With #all', function() { 634 | describe('this should refer to the bound object', function() { 635 | specify('after all with immediate values', function() { 636 | return fulfilled([1, 2, 3]).bind(THIS).all().then(function(v) { 637 | assert(v.length === 3); 638 | assert(this === THIS); 639 | }); 640 | }); 641 | specify('after all with eventual values', function() { 642 | var d1 = pending(); 643 | var p1 = d1.promise; 644 | 645 | var d2 = pending(); 646 | var p2 = d2.promise; 647 | 648 | var d3 = pending(); 649 | var p3 = d3.promise; 650 | 651 | setTimeout(function() { 652 | d1.resolve(1); 653 | d2.resolve(2); 654 | d3.resolve(3); 655 | }, 50); 656 | 657 | return fulfilled([p1, p2, p3]).bind(THIS).all().then(function(v) { 658 | assert(v.length === 3); 659 | assert(this === THIS); 660 | }); 661 | }); 662 | }); 663 | 664 | describe('this should not refer to the bound object', function() { 665 | specify('in the promises created within the handler', function() { 666 | var d1 = pending(); 667 | var p1 = d1.promise; 668 | 669 | setTimeout(function() { 670 | d1.resolve(1); 671 | }, 50); 672 | 673 | return fulfilled([1, 2, 3]).bind(THIS).all(function() { 674 | return Promise.all([p1]).then(function() { 675 | assert(this !== THIS); 676 | return 1; 677 | }); 678 | }).then(function() { 679 | assert(this === THIS); 680 | }); 681 | }); 682 | }); 683 | }); 684 | 685 | 686 | describe('With .join', function() { 687 | describe('this should refer to the bound object', function() { 688 | specify('after join with immediate values', function() { 689 | return Promise.bind(THIS).constructor.join(1, 2, 3).then(function(v) { 690 | assert(v.length === 3); 691 | assert(this === THIS); 692 | }); 693 | }); 694 | specify('after join with eventual values', function() { 695 | var d1 = pending(); 696 | var p1 = d1.promise; 697 | 698 | var d2 = pending(); 699 | var p2 = d2.promise; 700 | 701 | var d3 = pending(); 702 | var p3 = d3.promise; 703 | 704 | setTimeout(function() { 705 | d1.resolve(1); 706 | d2.resolve(2); 707 | d3.resolve(3); 708 | }, 50); 709 | 710 | return Promise.bind(THIS).constructor.join(p1, p2, p3).then(function(v) { 711 | assert(v.length === 3); 712 | assert(this === THIS); 713 | }); 714 | }); 715 | }); 716 | 717 | describe('this should not refer to the bound object', function() { 718 | specify('in the promises created within the handler', function() { 719 | var d1 = pending(); 720 | var p1 = d1.promise; 721 | 722 | setTimeout(function() { 723 | d1.resolve(1); 724 | }, 50); 725 | 726 | return Promise.bind(THIS).constructor.join(1, 2, 3).then(function() { 727 | return Promise.all([p1]).then(function() { 728 | assert(this !== THIS); 729 | return 1; 730 | }); 731 | }).then(function() { 732 | assert(this === THIS); 733 | }); 734 | }); 735 | }); 736 | }); 737 | 738 | 739 | describe('With #race', function() { 740 | describe('this should refer to the bound object', function() { 741 | specify('after race with immediate values', function() { 742 | return fulfilled([1, 2, 3]).bind(THIS).race().then(function(v) { 743 | assert(v === 1); 744 | assert(this === THIS); 745 | }); 746 | }); 747 | specify('after race with eventual values', function() { 748 | var d1 = pending(); 749 | var p1 = d1.promise; 750 | 751 | var d2 = pending(); 752 | var p2 = d2.promise; 753 | 754 | var d3 = pending(); 755 | var p3 = d3.promise; 756 | 757 | setTimeout(function() { 758 | d1.resolve(1); 759 | d2.resolve(2); 760 | d3.resolve(3); 761 | }, 50); 762 | 763 | return fulfilled([p1, p2, p3]).bind(THIS).race().then(function(v) { 764 | assert(v === 1); 765 | assert(this === THIS); 766 | }); 767 | }); 768 | }); 769 | 770 | describe('this should not refer to the bound object', function() { 771 | specify('in the promises created within the handler', function() { 772 | var d1 = pending(); 773 | var p1 = d1.promise; 774 | 775 | setTimeout(function() { 776 | d1.resolve(1); 777 | }, 50); 778 | 779 | return fulfilled([1, 2, 3]).bind(THIS).race(function() { 780 | return Promise.race([p1]).then(function() { 781 | assert(this !== THIS); 782 | return 1; 783 | }); 784 | }).then(function() { 785 | assert(this === THIS); 786 | }); 787 | }); 788 | }); 789 | }); 790 | 791 | 792 | describe('With #props', function() { 793 | describe('this should refer to the bound object', function() { 794 | specify('after props with immediate values', function() { 795 | return fulfilled([1, 2, 3]).bind(THIS).props().then(function(v) { 796 | assert(v[2] === 3); 797 | assert(this === THIS); 798 | }); 799 | }); 800 | specify('after props with eventual values', function() { 801 | var d1 = pending(); 802 | var p1 = d1.promise; 803 | 804 | var d2 = pending(); 805 | var p2 = d2.promise; 806 | 807 | var d3 = pending(); 808 | var p3 = d3.promise; 809 | 810 | setTimeout(function() { 811 | d1.resolve(1); 812 | d2.resolve(2); 813 | d3.resolve(3); 814 | }, 50); 815 | 816 | return fulfilled([p1, p2, p3]).bind(THIS).props().then(function(v) { 817 | assert(v[2] === 3); 818 | assert(this === THIS); 819 | }); 820 | }); 821 | }); 822 | 823 | describe('this should not refer to the bound object', function() { 824 | specify('in the promises created within the handler', function() { 825 | var d1 = pending(); 826 | var p1 = d1.promise; 827 | 828 | setTimeout(function() { 829 | d1.resolve(1); 830 | }, 50); 831 | 832 | return fulfilled([1, 2, 3]).bind(THIS).props(function() { 833 | return Promise.settle([p1]).then(function() { 834 | assert(this !== THIS); 835 | return 1; 836 | }); 837 | }).then(function() { 838 | assert(this === THIS); 839 | }); 840 | }); 841 | }); 842 | }); 843 | 844 | specify('should not get confused', function() { 845 | var a = {}; 846 | var b = {}; 847 | var c = {}; 848 | var dones = 0; 849 | 850 | return Promise.bind(a).then(function() { 851 | assert(this === a); 852 | ++dones; 853 | }).bind(b).then(function() { 854 | assert(this === b); 855 | ++dones; 856 | }).bind(c).then(function() { 857 | assert(this === c); 858 | ++dones; 859 | }).then(function() { 860 | assert(dones === 3); 861 | }); 862 | }); 863 | }); 864 | -------------------------------------------------------------------------------- /test/caught.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /* eslint no-unused-vars: ["error", { "args": "none" }] */ 3 | 'use strict'; 4 | 5 | var assert = require('assert'); 6 | var Promise = require('../'); 7 | 8 | var pending = Promise.defer.bind(Promise); 9 | 10 | var CustomError = function() {}; 11 | CustomError.prototype = new Error(); 12 | 13 | var predicateFilter = function(e) { 14 | return (/invalid/).test(e.message); 15 | }; 16 | 17 | function predicatesUndefined(e) { 18 | return e === void 0; 19 | } 20 | 21 | function predicatesPrimitiveString(e) { 22 | return /^asd$/.test(e); 23 | } 24 | 25 | describe('A promise handler that throws a TypeError must be caught', function() { 26 | 27 | specify('in a middle.caught filter', function() { 28 | var a = pending(); 29 | var caught = false; 30 | 31 | setTimeout(function() { a.resolve(3); }, 10); 32 | 33 | return a.promise.then(function() { 34 | a.b.c.d(); 35 | }).caught(SyntaxError, function(e) { 36 | assert.fail(); 37 | }).caught(TypeError, function(e) { 38 | caught = true; 39 | }).caught(function(e) { 40 | assert.fail(); 41 | }).then(function() { 42 | assert(caught); 43 | }); 44 | }); 45 | 46 | 47 | specify('in a generic.caught filter that comes first', function() { 48 | var a = pending(); 49 | var caught = false; 50 | 51 | setTimeout(function() { a.resolve(3); }, 10); 52 | 53 | return a.promise.then(function() { 54 | a.b.c.d(); 55 | }).caught(function(e) { 56 | caught = true; 57 | }).caught(SyntaxError, function(e) { 58 | assert.fail(); 59 | }).caught(TypeError, function(e) { 60 | assert.fail(); 61 | }).then(function() { 62 | assert(caught); 63 | }); 64 | }); 65 | 66 | specify('in an explicitly generic.caught filter that comes first', function() { 67 | var a = pending(); 68 | var caught = false; 69 | 70 | setTimeout(function() { a.resolve(3); }, 10); 71 | 72 | return a.promise.then(function() { 73 | a.b.c.d(); 74 | }).caught(Error, function(e) { 75 | caught = true; 76 | }).caught(SyntaxError, function(e) { 77 | assert.fail(); 78 | }).caught(Promise.TypeError, function(e) { 79 | assert.fail(); 80 | }).then(function() { 81 | assert(caught); 82 | }); 83 | }); 84 | 85 | specify('in a specific handler after thrown in generic', function() { 86 | var a = pending(); 87 | var caught = false; 88 | 89 | setTimeout(function() { a.resolve(3); }, 10); 90 | 91 | return a.promise.then(function() { 92 | a.b.c.d(); 93 | }).caught(function(e) { 94 | throw e; 95 | }).caught(SyntaxError, function(e) { 96 | assert.fail(); 97 | }).caught(TypeError, function(e) { 98 | caught = true; 99 | }).caught(function(e) { 100 | assert.fail(); 101 | }).then(function() { 102 | assert(caught); 103 | }); 104 | }); 105 | 106 | 107 | specify('in a multi-filter handler', function() { 108 | var a = pending(); 109 | var caught = false; 110 | 111 | setTimeout(function() { a.resolve(3); }, 10); 112 | 113 | return a.promise.then(function() { 114 | a.b.c.d(); 115 | }).caught(SyntaxError, TypeError, function(e) { 116 | caught = true; 117 | }).caught(function(e) { 118 | assert.fail(); 119 | }).then(function() { 120 | assert(caught); 121 | }); 122 | }); 123 | 124 | 125 | specify('in a specific handler after non-matching multi.caught handler', function() { 126 | var a = pending(); 127 | var caught = false; 128 | 129 | setTimeout(function() { a.resolve(3); }, 10); 130 | 131 | return a.promise.then(function() { 132 | a.b.c.d(); 133 | }).caught(SyntaxError, CustomError, function(e) { 134 | assert.fail(); 135 | }).caught(TypeError, function(e) { 136 | caught = true; 137 | }).caught(function(e) { 138 | assert.fail(); 139 | }).then(function() { 140 | assert(caught); 141 | }); 142 | }); 143 | 144 | }); 145 | 146 | 147 | describe('A promise handler that throws a custom error', function() { 148 | 149 | specify('Is filtered if inheritance was done even remotely properly', function() { 150 | var a = pending(); 151 | var b = new CustomError(); 152 | var caught = false; 153 | 154 | setTimeout(function() { a.resolve(3); }, 10); 155 | 156 | return a.promise.then(function() { 157 | throw b; 158 | }).caught(SyntaxError, function(e) { 159 | assert.fail(); 160 | }).caught(TypeError, function(e) { 161 | assert.fail(); 162 | }).caught(CustomError, function(e) { 163 | assert.equal(e, b); 164 | caught = true; 165 | }).then(function() { 166 | assert(caught); 167 | }); 168 | }); 169 | 170 | specify('Is filtered along with built-in errors', function() { 171 | var a = pending(); 172 | var b = new CustomError(); 173 | var caught = false; 174 | 175 | setTimeout(function() { a.resolve(3); }, 10); 176 | 177 | return a.promise.then(function() { 178 | throw b; 179 | }).caught(TypeError, SyntaxError, CustomError, function(e) { 180 | caught = true; 181 | }).caught( 182 | assert.fail 183 | ).then(function() { 184 | assert(caught); 185 | }); 186 | }); 187 | }); 188 | 189 | describe('A promise handler that throws a CustomError must be caught', function() { 190 | specify('in a middle.caught filter', function() { 191 | var a = pending(); 192 | var caught = false; 193 | 194 | setTimeout(function() { a.resolve(3); }, 10); 195 | 196 | return a.promise.then(function() { 197 | throw new CustomError(); 198 | }).caught(SyntaxError, function(e) { 199 | assert.fail(); 200 | }).caught(CustomError, function(e) { 201 | caught = true; 202 | }).caught(function(e) { 203 | assert.fail(); 204 | }).then(function() { 205 | assert(caught); 206 | }); 207 | }); 208 | 209 | 210 | specify('in a generic.caught filter that comes first', function() { 211 | var a = pending(); 212 | var caught = false; 213 | 214 | setTimeout(function() { a.resolve(3); }, 10); 215 | 216 | return a.promise.then(function() { 217 | throw new CustomError(); 218 | }).caught(function(e) { 219 | caught = true; 220 | }).caught(SyntaxError, function(e) { 221 | assert.fail(); 222 | }).caught(CustomError, function(e) { 223 | assert.fail(); 224 | }).then(function() { 225 | assert(caught); 226 | }); 227 | }); 228 | 229 | specify('in an explicitly generic.caught filter that comes first', function() { 230 | var a = pending(); 231 | var caught = false; 232 | 233 | setTimeout(function() { a.resolve(3); }, 10); 234 | 235 | return a.promise.then(function() { 236 | throw new CustomError(); 237 | }).caught(Error, function(e) { 238 | caught = true; 239 | }).caught(SyntaxError, function(e) { 240 | assert.fail(); 241 | }).caught(CustomError, function(e) { 242 | assert.fail(); 243 | }).then(function() { 244 | assert(caught); 245 | }); 246 | }); 247 | 248 | specify('in a specific handler after thrown in generic', function() { 249 | var a = pending(); 250 | var caught = false; 251 | 252 | setTimeout(function() { a.resolve(3); }, 10); 253 | 254 | return a.promise.then(function() { 255 | throw new CustomError(); 256 | }).caught(function(e) { 257 | throw e; 258 | }).caught(SyntaxError, function(e) { 259 | assert.fail(); 260 | }).caught(CustomError, function(e) { 261 | caught = true; 262 | }).caught(function(e) { 263 | assert.fail(); 264 | }).then(function() { 265 | assert(caught); 266 | }); 267 | }); 268 | 269 | 270 | specify('in a multi-filter handler', function() { 271 | var a = pending(); 272 | var caught = false; 273 | 274 | setTimeout(function() { a.resolve(3); }, 10); 275 | 276 | return a.promise.then(function() { 277 | throw new CustomError(); 278 | }).caught(SyntaxError, CustomError, function(e) { 279 | caught = true; 280 | }).caught(function(e) { 281 | assert.fail(); 282 | }).then(function() { 283 | assert(caught); 284 | }); 285 | }); 286 | 287 | 288 | specify('in a specific handler after non-matching multi.caught handler', function() { 289 | var a = pending(); 290 | var caught = false; 291 | 292 | setTimeout(function() { a.resolve(3); }, 10); 293 | 294 | return a.promise.then(function() { 295 | throw new CustomError(); 296 | }).caught(SyntaxError, TypeError, function(e) { 297 | assert.fail(); 298 | }).caught(CustomError, function(e) { 299 | caught = true; 300 | }).caught(function(e) { 301 | assert.fail(); 302 | }).then(function() { 303 | assert(caught); 304 | }); 305 | }); 306 | 307 | }); 308 | 309 | describe('A promise handler that is caught in a filter', function() { 310 | 311 | specify('is continued normally after returning a promise in filter', function() { 312 | var a = pending(); 313 | var c = pending(); 314 | var b = new CustomError(); 315 | 316 | setTimeout(function() { a.resolve(3); }, 10); 317 | setTimeout(function() { c.resolve(3); }, 20); 318 | 319 | return a.promise.then(function(v) { 320 | assert.equal(v, 3); 321 | throw b; 322 | }).caught(SyntaxError, function(e) { 323 | assert.fail(); 324 | }).caught(TypeError, function(e) { 325 | assert.fail(); 326 | }).caught(CustomError, function(e) { 327 | assert.equal(e, b); 328 | return c.promise; 329 | }).then(function(v) { 330 | assert.equal(v, 3); 331 | }); 332 | }); 333 | 334 | specify('is continued normally after returning a promise in original handler', function() { 335 | var a = pending(); 336 | var c = pending(); 337 | 338 | setTimeout(function() { a.resolve(3); }, 10); 339 | setTimeout(function() { c.resolve(3); }, 20); 340 | 341 | return a.promise.then(function(v) { 342 | assert.equal(v, 3); 343 | return c.promise; 344 | }).caught(SyntaxError, function(e) { 345 | assert.fail(); 346 | }).caught(TypeError, function(e) { 347 | assert.fail(); 348 | }).caught(CustomError, function(e) { 349 | assert.fail(); 350 | }).then(function(v) { 351 | assert.equal(v, 3); 352 | }); 353 | }); 354 | }); 355 | 356 | describe('A promise handler with a predicate filter', function() { 357 | 358 | specify('will catch a thrown thing matching the filter', function() { 359 | var a = pending(); 360 | var caught = false; 361 | 362 | setTimeout(function() { a.resolve(3); }, 10); 363 | 364 | return a.promise.then(function() { 365 | throw new Error('horrible invalid error string'); 366 | }).caught(predicateFilter, function(e) { 367 | caught = true; 368 | }).caught(function(e) { 369 | assert.fail(); 370 | }).then(function() { 371 | assert(caught); 372 | }); 373 | }); 374 | 375 | specify('will NOT catch a thrown thing not matching the filter', function() { 376 | var a = pending(); 377 | var caught = false; 378 | 379 | setTimeout(function() { a.resolve(3); }, 10); 380 | 381 | return a.promise.then(function() { 382 | throw new Error('horrible valid error string'); 383 | }).caught(predicateFilter, function(e) { 384 | assert.fail(); 385 | }).caught(function(e) { 386 | caught = true; 387 | }).then(function() { 388 | assert(caught); 389 | }); 390 | }); 391 | 392 | specify('will fail when a predicate is a bad error class', function() { 393 | var a = pending(); 394 | var caught = false; 395 | 396 | setTimeout(function() { a.resolve(3); }, 10); 397 | 398 | return a.promise.then(function() { 399 | throw new Error('horrible custom error'); 400 | }).caught(123, function(e) { 401 | assert.fail(); 402 | }).caught(TypeError, function(e) { 403 | // Uncomment to show the TypeError stack 404 | // console.error(e.stack); 405 | caught = true; 406 | }).then(function() { 407 | assert(caught); 408 | }); 409 | }); 410 | 411 | specify('will catch a thrown undefiend', function() { 412 | var a = pending(); 413 | var caught = false; 414 | 415 | setTimeout(function() { a.resolve(3); }, 10); 416 | 417 | return a.promise.then(function() { 418 | throw void 0; 419 | }).caught(function(e) { return false; }, function(e) { 420 | assert.fail(); 421 | }).caught(predicatesUndefined, function(e) { 422 | caught = true; 423 | }).caught(function(e) { 424 | assert.fail(); 425 | }).then(function() { 426 | assert(caught); 427 | }); 428 | }); 429 | 430 | specify('will catch a thrown string', function() { 431 | var a = pending(); 432 | var caught = false; 433 | 434 | setTimeout(function() { a.resolve(3); }, 10); 435 | 436 | return a.promise.then(function() { 437 | throw 'asd'; 438 | }).caught(function(e) { return false; }, function(e) { 439 | assert.fail(); 440 | }).caught(predicatesPrimitiveString, function(e) { 441 | caught = true; 442 | }).caught(function(e) { 443 | assert.fail(); 444 | }).then(function() { 445 | assert(caught); 446 | }); 447 | }); 448 | 449 | specify('will fail when a predicate throws', function() { 450 | var a = pending(); 451 | var caught = false; 452 | 453 | setTimeout(function() { a.resolve(3); }, 10); 454 | 455 | return a.promise.then(function() { 456 | throw new CustomError('error happens'); 457 | }).caught(function(e) { return e.f.g; }, function(e) { 458 | assert.fail(); 459 | }).caught(TypeError, function(e) { 460 | // Uncomment to show the TypeError stack 461 | // console.error(e.stack); 462 | caught = true; 463 | }).caught(function(e) { 464 | assert.fail(); 465 | }).then(function() { 466 | assert(caught); 467 | }); 468 | }); 469 | 470 | }); 471 | -------------------------------------------------------------------------------- /test/filter.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: ["error", { "args": "none" }] */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | describe('Promise filter', function() { 8 | 9 | function ThrownError() {} 10 | 11 | 12 | var arr = [1, 2, 3]; 13 | 14 | function assertArr(arr) { 15 | assert(arr.length === 2); 16 | assert(arr[0] === 1); 17 | assert(arr[1] === 3); 18 | } 19 | 20 | function assertErr(e) { 21 | assert(e instanceof ThrownError); 22 | } 23 | 24 | function assertFail() { 25 | assert.fail(); 26 | } 27 | 28 | describe('should accept eventual booleans', function() { 29 | specify('immediately fulfilled', function() { 30 | return Promise.filter(arr, function(v) { 31 | return new Promise(function(r) { 32 | r(v !== 2); 33 | }); 34 | }).then(assertArr); 35 | }); 36 | 37 | specify('already fulfilled', function() { 38 | return Promise.filter(arr, function(v) { 39 | return Promise.resolve(v !== 2); 40 | }).then(assertArr); 41 | }); 42 | 43 | specify('eventually fulfilled', function() { 44 | return Promise.filter(arr, function(v) { 45 | return new Promise(function(r) { 46 | setTimeout(function() { 47 | r(v !== 2); 48 | }, 13); 49 | }); 50 | }).then(assertArr); 51 | }); 52 | 53 | specify('immediately rejected', function() { 54 | return Promise.filter(arr, function(v) { 55 | return new Promise(function(v, r) { 56 | r(new ThrownError()); 57 | }); 58 | }).then(assertFail, assertErr); 59 | }); 60 | specify('already rejected', function() { 61 | return Promise.filter(arr, function(v) { 62 | return Promise.reject(new ThrownError()); 63 | }).then(assertFail, assertErr); 64 | }); 65 | specify('eventually rejected', function() { 66 | return Promise.filter(arr, function(v) { 67 | return new Promise(function(v, r) { 68 | setTimeout(function() { 69 | r(new ThrownError()); 70 | }, 13); 71 | }); 72 | }).then(assertFail, assertErr); 73 | }); 74 | 75 | 76 | specify('immediately fulfilled thenable', function() { 77 | return Promise.filter(arr, function(v) { 78 | return { 79 | then: function(f, r) { 80 | f(v !== 2); 81 | }, 82 | }; 83 | }).then(assertArr); 84 | }); 85 | specify('eventually fulfilled thenable', function() { 86 | return Promise.filter(arr, function(v) { 87 | return { 88 | then: function(f, r) { 89 | setTimeout(function() { 90 | f(v !== 2); 91 | }, 13); 92 | }, 93 | }; 94 | }).then(assertArr); 95 | }); 96 | 97 | specify('immediately rejected thenable', function() { 98 | return Promise.filter(arr, function(v) { 99 | return { 100 | then: function(f, r) { 101 | r(new ThrownError()); 102 | }, 103 | }; 104 | }).then(assertFail, assertErr); 105 | }); 106 | specify('eventually rejected thenable', function() { 107 | return Promise.filter(arr, function(v) { 108 | return { 109 | then: function(f, r) { 110 | setTimeout(function() { 111 | r(new ThrownError()); 112 | }, 13); 113 | }, 114 | }; 115 | }).then(assertFail, assertErr); 116 | }); 117 | 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/generator.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len, no-unused-vars */ 2 | 'use strict'; 3 | 4 | // Bail if we're not running in node >= 0.11 5 | var ver = process.versions.node.split('.').map(function(s) { 6 | return parseInt(s, 10); 7 | }); 8 | var node11 = ver[0] > 0 || ver[1] >= 11; 9 | if (!node11) { return; } 10 | 11 | var assert = require('assert'); 12 | var Promise = require('../'); 13 | 14 | function get(arg) { 15 | return { 16 | then: function(ful, rej) { 17 | ful(arg); 18 | }, 19 | }; 20 | } 21 | 22 | function fail(arg) { 23 | return { 24 | then: function(ful, rej) { 25 | rej(arg); 26 | }, 27 | }; 28 | } 29 | 30 | function delay() { 31 | return new Promise(function(a) { 32 | setTimeout(a, 15); 33 | }); 34 | } 35 | 36 | var error = new Error('asd'); 37 | 38 | describe('Promise.async', function() { 39 | describe('thenables', function() { 40 | specify('when they fulfill, the yielded value should be that fulfilled value', function() { 41 | 42 | return Promise.async(eval('(function*(){' + 43 | 44 | 'var a = yield get(3);' + 45 | 'assert.equal(a, 3);' + 46 | 'return 4;' + 47 | 48 | '})'))().then(function(arg) { 49 | assert.equal(arg, 4); 50 | }); 51 | 52 | }); 53 | 54 | 55 | specify("when they reject, and the generator doesn't have try.caught, it should immediately reject the promise", function() { 56 | 57 | return Promise.async(eval('(function*(){' + 58 | 'var a = yield fail(error);' + 59 | 'assert.fail();' + 60 | 61 | '})'))().then(assert.fail, function(e) { 62 | assert.equal(e, error); 63 | }); 64 | 65 | }); 66 | 67 | specify('when they reject, and the generator has try.caught, it should continue working normally', function() { 68 | 69 | return Promise.async(eval('(function*(){' + 70 | 'try {' + 71 | ' var a = yield fail(error);' + 72 | '} catch(e) {' + 73 | ' return e;' + 74 | '}' + 75 | 'assert.fail();' + 76 | '})'))().then(function(v) { 77 | assert.equal(v, error); 78 | }); 79 | 80 | }); 81 | 82 | specify('when they fulfill but then throw, it should become rejection', function() { 83 | 84 | return Promise.async(eval('(function*(){' + 85 | 'var a = yield get(3);' + 86 | 'assert.equal(a, 3);' + 87 | 'throw error;' + 88 | '})'))().then(assert.fail, function(e) { 89 | assert.equal(e, error); 90 | }); 91 | }); 92 | }); 93 | 94 | describe('yield loop', function() { 95 | 96 | specify('should work', function() { 97 | return Promise.async(eval('(function*() {' + 98 | 'var a = [1,2,3,4,5];' + 99 | 100 | 'for( var i = 0, len = a.length; i < len; ++i ) {' + 101 | ' a[i] = yield get(a[i] * 2);' + 102 | '}' + 103 | 104 | 'return a;' + 105 | '})'))().then(function(arr) { 106 | assert.deepEqual([2, 4, 6, 8, 10], arr); 107 | }); 108 | }); 109 | 110 | specify('inside yield should work', function() { 111 | return Promise.async(eval('(function*() {' + 112 | 'var a = [1,2,3,4,5];' + 113 | 114 | 'return yield Promise.all(a.map(function(v){' + 115 | ' return Promise.async(function *() {' + 116 | ' return yield get(v*2);' + 117 | ' })();' + 118 | '}));' + 119 | '})'))().then(function(arr) { 120 | assert.deepEqual([2, 4, 6, 8, 10], arr); 121 | }); 122 | }); 123 | 124 | specify('with simple map should work', function() { 125 | return Promise.async(eval('(function*() {' + 126 | 'var a = [1,2,3,4,5];' + 127 | 128 | 'return yield Promise.map(a, function(v){' + 129 | ' return Promise.resolve(get(v*2));' + 130 | '});' + 131 | '})'))().then(function(arr) { 132 | assert.deepEqual([2, 4, 6, 8, 10], arr); 133 | }); 134 | }); 135 | 136 | }); 137 | 138 | describe('when using async as a method', function() { 139 | 140 | function MyClass() { 141 | this.goblins = 3; 142 | } 143 | 144 | MyClass.prototype.noOp = Promise.async(eval('(function*(){})')); 145 | MyClass.prototype.spawnGoblins = Promise.async(eval('(function*(){' + 146 | ' this.goblins = yield get(this.goblins+1);' + 147 | '})')); 148 | 149 | 150 | specify('should always return a promise', function() { 151 | var a = new MyClass(); 152 | assert(a.noOp() instanceof Promise); 153 | return a.noOp().then(function(v) { 154 | assert.equal(v, undefined); 155 | }); 156 | }); 157 | 158 | specify("generator function's receiver should be the instance too", function() { 159 | var a = new MyClass(); 160 | var b = new MyClass(); 161 | 162 | return Promise.join(a.spawnGoblins().then(function() { 163 | return a.spawnGoblins(); 164 | }), b.spawnGoblins()).then(function() { 165 | assert.equal(a.goblins, 5); 166 | assert.equal(b.goblins, 4); 167 | }); 168 | 169 | }); 170 | }); 171 | 172 | describe('caution from README', function() { 173 | var thrower = Promise.method(function(msg) { throw new Error(msg); }); 174 | 175 | specify('return promise-which-rejects does not catch', function() { 176 | var func1 = Promise.async(eval('(function *() {' + 177 | 'try {' + 178 | ' return thrower("hey");' + 179 | '} catch (e) {' + 180 | ' assert(false); /* this line is never reached */' + 181 | '}' + 182 | '})')); 183 | return func1().then(assert.fail, function(e) { 184 | assert.equal(e.message, 'hey'); 185 | }); 186 | }); 187 | 188 | specify('return yield-promise-which-rejects does catch', function() { 189 | var func1 = Promise.async(eval('(function *() {' + 190 | 'try {' + 191 | ' return (yield thrower("ho"));' + 192 | '} catch (e) {' + 193 | ' assert.equal(e.message, "ho");' + 194 | ' return "caught";' + 195 | '}' + 196 | '})')); 197 | return func1().then(function(v) { 198 | assert.equal(v, 'caught'); 199 | }); 200 | }); 201 | }); 202 | 203 | describe('legacy callbacks', function() { 204 | var getDataFor = Promise.async(eval( 205 | '(function *(input) {' + 206 | 'yield Promise.resolve();' + 207 | 'if (!input) throw new Error("no input");' + 208 | 'return input;' + 209 | '})'), 1 /* Arg #1 is optional callback */); 210 | 211 | specify('should return Promise', function() { 212 | var p = getDataFor(5); 213 | assert(p instanceof Promise); 214 | return p.then(function(v) { 215 | assert.equal(v, 5); 216 | }); 217 | }); 218 | 219 | specify('should accept callback for value', function() { 220 | return new Promise(function(resolve, reject) { 221 | getDataFor(6, function(err, v) { 222 | if (err) { 223 | return reject(err); 224 | } 225 | resolve(v); 226 | }); 227 | }).then(function(vv) { assert.equal(vv, 6); }); 228 | }); 229 | 230 | specify('should accept callback for exception', function() { 231 | return new Promise(function(resolve, reject) { 232 | getDataFor(0, function(err, v) { 233 | if (err) { 234 | return resolve(err); // An error is the expected result! 235 | } 236 | reject(new Error(v)); 237 | }); 238 | }).then(function(vv) { assert(vv.message, 'no input'); }); 239 | }); 240 | }); 241 | }); 242 | -------------------------------------------------------------------------------- /test/guard.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | var sentinel = {}; 8 | var other = {}; 9 | 10 | function noop() {} 11 | 12 | var mkspy = function(calls) { 13 | return function spy() { 14 | var args = Array.prototype.slice.call(arguments); 15 | args.unshift(this); 16 | calls.push(args); 17 | }; 18 | }; 19 | 20 | describe('Promise.guard', function() { 21 | 22 | it('should return a function', function() { 23 | assert.equal(typeof Promise.guard(), 'function'); 24 | // Return a promise from all synchronous tests, for consistency 25 | return Promise.resolve(); 26 | }); 27 | 28 | it('should invoke condition', function() { 29 | var condition; 30 | var guarded; 31 | var called = 0; 32 | 33 | condition = function() { called++; }; 34 | 35 | guarded = Promise.guard(condition, noop); 36 | 37 | guarded(); 38 | 39 | assert.equal(called, 1); 40 | 41 | // Return a promise from all synchronous tests, for consistency 42 | return Promise.resolve(); 43 | }); 44 | 45 | it('should invoke guarded function after condition promise fulfills', function() { 46 | var condition, f, guarded; 47 | var calls = []; 48 | 49 | condition = function() { return noop; }; 50 | f = mkspy(calls); 51 | guarded = Promise.guard(condition, f); 52 | 53 | return guarded.call(null, sentinel).then(function() { 54 | assert.deepEqual(calls, [[null, sentinel]]); 55 | }); 56 | }); 57 | 58 | it('should notify condition once guarded function settles', function() { 59 | var condition, notify, guarded; 60 | var calls = []; 61 | 62 | notify = mkspy(calls); 63 | condition = function() { return notify; }; 64 | guarded = Promise.guard(condition, noop); 65 | 66 | return guarded().then(function() { 67 | assert.equal(calls.length, 1); 68 | }); 69 | }); 70 | 71 | it('should initiate next guarded call after notify', function() { 72 | var condition, f, guarded; 73 | var calls = []; 74 | 75 | f = mkspy(calls); 76 | condition = function() { return noop; }; 77 | guarded = Promise.guard(condition, f); 78 | 79 | return guarded(other).then(function() { 80 | assert.equal(calls.length, 1); 81 | return guarded(sentinel).then(function() { 82 | assert.equal(calls.length, 2); 83 | assert.deepEqual(calls[1], [undefined, sentinel]); 84 | }); 85 | }); 86 | }); 87 | 88 | describe('n', function() { 89 | it('should create a function', function() { 90 | assert.equal(typeof Promise.guard.n(1), 'function'); 91 | // Return a promise from all synchronous tests, for consistency 92 | return Promise.resolve(); 93 | }); 94 | 95 | it('should return a promise', function() { 96 | var c = Promise.guard.n(1); 97 | assert.equal(typeof c().then, 'function'); 98 | // Return a promise from all synchronous tests, for consistency 99 | return Promise.resolve(); 100 | }); 101 | 102 | it('returned promise should resolve to a function', function() { 103 | var enter = Promise.guard.n(1); 104 | return enter().then(function(exit) { 105 | assert.equal(typeof exit, 'function'); 106 | }); 107 | }); 108 | 109 | it('should allow one execution', function() { 110 | var enter, value, first, second; 111 | 112 | enter = Promise.guard.n(1); 113 | value = sentinel; 114 | 115 | first = enter(); 116 | second = enter(); 117 | 118 | var p1 = first.then(function(exit) { 119 | return Promise.delay(50).then(function() { 120 | assert.strictEqual(value, sentinel); 121 | exit(); 122 | }); 123 | }); 124 | 125 | var p2 = second.then(function() { 126 | value = other; 127 | }); 128 | 129 | return Promise.join(p1, p2); 130 | }); 131 | 132 | it('should allow two executions', function() { 133 | var one, value, first, second, third; 134 | 135 | one = Promise.guard.n(2); 136 | value = sentinel; 137 | 138 | first = one(); 139 | second = one(); 140 | third = one(); 141 | 142 | var p1 = first.then(function() { 143 | assert.strictEqual(value, sentinel); 144 | }); 145 | 146 | var p2 = second.then(function(exit) { 147 | return Promise.delay(50).then(function() { 148 | assert.strictEqual(value, sentinel); 149 | exit(); 150 | }); 151 | }); 152 | 153 | var p3 = third.then(function() { value = other; }); 154 | 155 | return Promise.join(p1, p2, p3); 156 | }); 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /test/method.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('../'); 5 | 6 | var fulfilled = Promise.resolve.bind(Promise); 7 | var pending = Promise.defer.bind(Promise); 8 | 9 | var obj = {}; 10 | var error = new Error(); 11 | 12 | describe('Promise.method', function() { 13 | 14 | var thrower = Promise.method(function() { 15 | throw error; 16 | }); 17 | 18 | var identity = Promise.method(function(val) { 19 | return val; 20 | }); 21 | 22 | var array = Promise.method(function() { 23 | return [].slice.call(arguments); 24 | }); 25 | 26 | var receiver = Promise.method(function() { 27 | return this; 28 | }); 29 | 30 | specify('should reject when the function throws', function() { 31 | var async = false; 32 | var p = thrower().then(assert.fail, function(e) { 33 | assert(async); 34 | assert(e === error); 35 | }); 36 | async = true; 37 | return p; 38 | }); 39 | 40 | specify('should throw when the function is not a function', function() { 41 | try { 42 | Promise.method(null); 43 | assert.fail(); 44 | } catch (e) { 45 | assert(e instanceof TypeError); 46 | } 47 | // Return a promise from all synchronous tests, for consistency 48 | return Promise.resolve(); 49 | }); 50 | 51 | specify('should call the function with the given receiver', function() { 52 | var async = false; 53 | var p = receiver.call(obj).then(function(val) { 54 | assert(async); 55 | assert(val === obj); 56 | }); 57 | async = true; 58 | return p; 59 | }); 60 | 61 | specify('should call the function with the given value', function() { 62 | var async = false; 63 | var p = identity(obj).then(function(val) { 64 | assert(async); 65 | assert(val === obj); 66 | }); 67 | async = true; 68 | return p; 69 | }); 70 | 71 | specify('should apply the function if given value is array', function() { 72 | var async = false; 73 | var p = array(1, 2, 3).then(function(val) { 74 | assert(async); 75 | assert.deepEqual(val, [1, 2, 3]); 76 | }); 77 | async = true; 78 | return p; 79 | }); 80 | 81 | specify('should unwrap returned promise', function() { 82 | var d = pending(); 83 | 84 | var p = Promise.method(function() { 85 | return d.promise; 86 | })().then(function(v) { 87 | assert.deepEqual(v, 3); 88 | }); 89 | 90 | setTimeout(function() { 91 | d.resolve(3); 92 | }, 13); 93 | return p; 94 | }); 95 | 96 | specify('should unwrap returned thenable', function() { 97 | return Promise.method(function() { 98 | return { 99 | then: function(f, v) { // eslint-disable-line no-unused-vars 100 | f(3); 101 | }, 102 | }; 103 | })().then(function(v) { 104 | assert.deepEqual(v, 3); 105 | }); 106 | }); 107 | 108 | specify('should unwrap this and arguments', function() { 109 | var THIS = {}; 110 | var pThis = pending(); 111 | var f = Promise.method(function(v) { 112 | assert(this === THIS); 113 | assert(v === 42); 114 | }); 115 | var p = f.call(pThis.promise, fulfilled(42)); 116 | setTimeout(function() { 117 | pThis.resolve(THIS); 118 | }, 10); 119 | return p; 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --check-leaks 2 | --reporter spec 3 | --require ./mocha-support 4 | -------------------------------------------------------------------------------- /test/promisify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | var RejectionError = function() {}; 8 | 9 | var sentinel = {}; 10 | var sentinelError = new RejectionError(); 11 | 12 | var erroneousNode = function(a, b, c, cb) { 13 | setTimeout(function() { 14 | cb(sentinelError); 15 | }, 10); 16 | }; 17 | 18 | var successNode = function(a, b, c, cb) { 19 | setTimeout(function() { 20 | cb(null, sentinel); 21 | }, 10); 22 | }; 23 | 24 | var successNodeMultipleValues = function(a, b, c, cb) { 25 | setTimeout(function() { 26 | cb(null, sentinel, sentinel, sentinel); 27 | }, 10); 28 | }; 29 | 30 | var syncErroneousNode = function(a, b, c, cb) { 31 | cb(sentinelError); 32 | }; 33 | 34 | var syncSuccessNode = function(a, b, c, cb) { 35 | cb(null, sentinel); 36 | }; 37 | 38 | var syncSuccessNodeMultipleValues = function(a, b, c, cb) { 39 | cb(null, sentinel, sentinel, sentinel); 40 | }; 41 | 42 | var errToThrow; 43 | var thrower = Promise.promisify(function(a, b, c, cb) { // eslint-disable-line no-unused-vars 44 | errToThrow = new RejectionError(); 45 | throw errToThrow; 46 | }); 47 | 48 | var tprimitive = 'Where is your stack now?'; 49 | var throwsStrings = Promise.promisify(function(cb) { // eslint-disable-line no-unused-vars 50 | throw tprimitive; 51 | }); 52 | 53 | var errbacksStrings = Promise.promisify(function(cb) { 54 | cb(tprimitive); 55 | }); 56 | 57 | var errbacksStringsAsync = Promise.promisify(function(cb) { 58 | setTimeout(function() { 59 | cb(tprimitive); 60 | }, 13); 61 | }); 62 | 63 | var error = Promise.promisify(erroneousNode); 64 | var success = Promise.promisify(successNode); 65 | var successMulti = Promise.promisify(successNodeMultipleValues, true); 66 | var successMulti2 = Promise.promisify(successNodeMultipleValues, ['a', 'b', 'c', 'd']); 67 | var syncError = Promise.promisify(syncErroneousNode); 68 | var syncSuccess = Promise.promisify(syncSuccessNode); 69 | var syncSuccessMulti = Promise.promisify(syncSuccessNodeMultipleValues, true); 70 | var syncSuccessMulti2 = Promise.promisify(syncSuccessNodeMultipleValues, ['a', 'b', 'c', 'd']); 71 | 72 | describe('when calling promisified function it should ', function() { 73 | 74 | 75 | specify('return a promise that is pending', function() { 76 | var pending = true; 77 | var a = error(1, 2, 3); 78 | var b = success(1, 2, 3); 79 | var c = successMulti(1, 2, 3); 80 | var d = successMulti2(1, 2, 3); 81 | 82 | a['finally'](function() { pending = false; }); 83 | b['finally'](function() { pending = false; }); 84 | c['finally'](function() { pending = false; }); 85 | d['finally'](function() { pending = false; }); 86 | 87 | assert.equal(pending, true); 88 | 89 | return a.then(assert.fail, function() { /* Caught */ }) 90 | .return(b).return(c).return(d).return(); 91 | }); 92 | 93 | specify('should use this if no receiver was given', function() { 94 | var o = {}; 95 | var fn = Promise.promisify(function(cb) { 96 | 97 | cb(null, this === o); 98 | }); 99 | 100 | o.fn = fn; 101 | 102 | return o.fn().then(function(val) { 103 | assert(val); 104 | }); 105 | }); 106 | 107 | specify('call future attached handlers later', function(done) { 108 | var a = error(1, 2, 3); 109 | var b = success(1, 2, 3); 110 | var c = successMulti(1, 2, 3); 111 | var c2 = successMulti2(1, 2, 3); 112 | var d = syncError(1, 2, 3); 113 | var e = syncSuccess(1, 2, 3); 114 | var f = syncSuccessMulti(1, 2, 3); 115 | var f2 = syncSuccessMulti2(1, 2, 3); 116 | var calls = 0; 117 | function donecall() { 118 | if ((++calls) === 8) { 119 | done(); 120 | } 121 | } 122 | 123 | a.caught(function() {}); 124 | d.caught(function() {}); 125 | 126 | setTimeout(function() { 127 | a.then(assert.fail, donecall); 128 | b.then(donecall, assert.fail); 129 | c.then(donecall, assert.fail); 130 | c2.then(donecall, assert.fail); 131 | d.then(assert.fail, donecall); 132 | e.then(donecall, assert.fail); 133 | f.then(donecall, assert.fail); 134 | f2.then(donecall, assert.fail); 135 | }, 20); 136 | }); 137 | 138 | specify('Reject with the synchronously caught reason', function() { 139 | return thrower(1, 2, 3).then(assert.fail, function(e) { 140 | assert(e === errToThrow); 141 | }); 142 | }); 143 | 144 | specify('reject with the proper reason', function(done) { 145 | var a = error(1, 2, 3); 146 | var b = syncError(1, 2, 3); 147 | var calls = 0; 148 | function donecall() { 149 | if ((++calls) === 2) { 150 | done(); 151 | } 152 | } 153 | 154 | a.caught(function(e) { 155 | assert.equal(sentinelError, e); 156 | donecall(); 157 | }); 158 | b.caught(function(e) { 159 | assert.equal(sentinelError, e); 160 | donecall(); 161 | }); 162 | }); 163 | 164 | specify('fulfill with proper value(s)', function() { 165 | var a = success(1, 2, 3); 166 | var b = successMulti(1, 2, 3); 167 | var b2 = successMulti2(1, 2, 3); 168 | var c = syncSuccess(1, 2, 3); 169 | var d = syncSuccessMulti(1, 2, 3); 170 | var d2 = syncSuccessMulti2(1, 2, 3); 171 | 172 | return Promise.join( 173 | 174 | a.then(function(val) { 175 | assert.equal(val, sentinel); 176 | }), 177 | 178 | b.then(function(val) { 179 | assert.deepEqual(val, [sentinel, sentinel, sentinel]); 180 | }), 181 | 182 | b2.then(function(val) { 183 | assert.deepEqual(val, { a: sentinel, b: sentinel, c: sentinel, d: undefined }); 184 | }), 185 | 186 | c.then(function(val) { 187 | assert.equal(val, sentinel); 188 | }), 189 | 190 | d.then(function(val) { 191 | assert.deepEqual(val, [sentinel, sentinel, sentinel]); 192 | }), 193 | 194 | d2.then(function(val) { 195 | assert.deepEqual(val, { a: sentinel, b: sentinel, c: sentinel, d: undefined }); 196 | }) 197 | 198 | ); 199 | }); 200 | 201 | 202 | }); 203 | 204 | 205 | describe('with more than 5 arguments', function() { 206 | 207 | var o = { 208 | value: 15, 209 | 210 | f: function(a, b, c, d, e, f, g, cb) { 211 | cb(null, [a, b, c, d, e, f, g, this.value]); 212 | }, 213 | 214 | }; 215 | 216 | var prom = Promise.promisify(o.f, false, o); 217 | 218 | specify('receiver should still work', function() { 219 | return prom(1, 2, 3, 4, 5, 6, 7).then(function(val) { 220 | assert.deepEqual( 221 | val, 222 | [1, 2, 3, 4, 5, 6, 7, 15] 223 | ); 224 | }); 225 | 226 | }); 227 | 228 | }); 229 | 230 | // In prfun, we don't wrap primitive errors. 231 | describe.skip('Primitive errors wrapping', function() { 232 | specify('when the node function throws it', function() { 233 | return throwsStrings().then(assert.fail, function(e) { 234 | assert(e instanceof Error); 235 | assert(e.message === tprimitive); 236 | }); 237 | }); 238 | 239 | specify('when the node function throws it inside then', function() { 240 | return Promise.resolve().then(function() { 241 | return throwsStrings().then(assert.fail, function(e) { 242 | assert(e instanceof Error); 243 | assert(e.message === tprimitive); 244 | }); 245 | }); 246 | }); 247 | 248 | 249 | specify('when the node function errbacks it synchronously', function() { 250 | return errbacksStrings().then(assert.fail, function(e) { 251 | assert(e instanceof Error); 252 | assert(e.message === tprimitive); 253 | }); 254 | }); 255 | 256 | specify('when the node function errbacks it synchronously inside then', function() { 257 | return Promise.resolve().then(function() { 258 | errbacksStrings().then(assert.fail, function(e) { 259 | assert(e instanceof Error); 260 | assert(e.message === tprimitive); 261 | }); 262 | }); 263 | }); 264 | 265 | specify('when the node function errbacks it asynchronously', function() { 266 | return errbacksStringsAsync().then(assert.fail, function(e) { 267 | assert(e instanceof Error); 268 | assert(e.message === tprimitive); 269 | }); 270 | }); 271 | 272 | specify('when the node function errbacks it asynchronously inside then', function() { 273 | return Promise.resolve().then(function() { 274 | errbacksStringsAsync().then(assert.fail, function(e) { 275 | assert(e instanceof Error); 276 | assert(e.message === tprimitive); 277 | }); 278 | }); 279 | }); 280 | }); 281 | 282 | // In prfun, we don't wrap primitive errors. 283 | // Also, we don't support Promise#error() 284 | describe.skip('RejectionError wrapping', function() { 285 | 286 | var CustomError = function() { }; 287 | 288 | CustomError.prototype = new Error(); 289 | CustomError.prototype.constructor = CustomError; 290 | 291 | function isUntypedError(obj) { 292 | return obj instanceof Error && 293 | Object.getPrototypeOf(obj) === Error.prototype; 294 | } 295 | 296 | 297 | if (!isUntypedError(new Error())) { 298 | console.log('error must be untyped'); 299 | } 300 | 301 | if (isUntypedError(new CustomError())) { 302 | console.log('customerror must be typed'); 303 | } 304 | 305 | var stringback = function(cb) { 306 | cb('Primitive as error'); 307 | }; 308 | 309 | var errback = function(cb) { 310 | cb(new Error('error as error')); 311 | }; 312 | 313 | var typeback = function(cb) { 314 | cb(new CustomError()); 315 | }; 316 | 317 | var stringthrow = function(cb) { // eslint-disable-line no-unused-vars 318 | throw ('Primitive as error'); 319 | }; 320 | 321 | var errthrow = function(cb) { // eslint-disable-line no-unused-vars 322 | throw (new Error('error as error')); 323 | }; 324 | 325 | var typethrow = function(cb) { // eslint-disable-line no-unused-vars 326 | throw (new CustomError()); 327 | }; 328 | 329 | stringback = Promise.promisify(stringback); 330 | errback = Promise.promisify(errback); 331 | typeback = Promise.promisify(typeback); 332 | stringthrow = Promise.promisify(stringthrow); 333 | errthrow = Promise.promisify(errthrow); 334 | typethrow = Promise.promisify(typethrow); 335 | 336 | specify('should wrap stringback', function(done) { 337 | stringback().error(function(e) { 338 | assert(e instanceof RejectionError); 339 | done(); 340 | }); 341 | }); 342 | 343 | specify('should wrap errback', function(done) { 344 | errback().error(function(e) { 345 | assert(e instanceof RejectionError); 346 | done(); 347 | }); 348 | }); 349 | 350 | specify('should not wrap typeback', function(done) { 351 | typeback().caught(CustomError, function(e) { // eslint-disable-line no-unused-vars 352 | done(); 353 | }); 354 | }); 355 | 356 | specify('should not wrap stringthrow', function(done) { 357 | stringthrow().error(assert.fail).caught(function(e) { 358 | assert(e instanceof Error); 359 | done(); 360 | }); 361 | }); 362 | 363 | specify('should not wrap errthrow', function(done) { 364 | errthrow().error(assert.fail).caught(function(e) { 365 | assert(e instanceof Error); 366 | done(); 367 | }); 368 | }); 369 | 370 | specify('should not wrap typethrow', function(done) { 371 | typethrow().error(assert.fail) 372 | .caught(CustomError, function(e) { // eslint-disable-line no-unused-vars 373 | done(); 374 | }); 375 | }); 376 | }); 377 | -------------------------------------------------------------------------------- /test/props.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('../'); 5 | 6 | var fulfilled = Promise.resolve.bind(Promise); 7 | var rejected = Promise.reject.bind(Promise); 8 | var pending = Promise.defer.bind(Promise); 9 | 10 | describe('Promise.props', function() { 11 | 12 | specify('should reject undefined', function() { 13 | return Promise.props().then(assert.fail, function(e) { 14 | assert(e instanceof TypeError); 15 | }); 16 | }); 17 | 18 | specify('should reject primitive', function() { 19 | return Promise.props('str').then(assert.fail, function(e) { 20 | assert(e instanceof TypeError); 21 | }); 22 | }); 23 | 24 | specify('should resolve to new object', function() { 25 | var o = {}; 26 | return Promise.props(o).then(function(v) { 27 | assert(v !== o); 28 | assert.deepEqual(o, v); 29 | }); 30 | }); 31 | 32 | specify('should resolve value properties', function() { 33 | var o = { 34 | one: 1, 35 | two: 2, 36 | three: 3, 37 | }; 38 | return Promise.props(o).then(function(v) { 39 | assert.deepEqual({ 40 | one: 1, 41 | two: 2, 42 | three: 3, 43 | }, v); 44 | }); 45 | }); 46 | 47 | specify('should resolve immediate properties', function() { 48 | var o = { 49 | one: fulfilled(1), 50 | two: fulfilled(2), 51 | three: fulfilled(3), 52 | }; 53 | return Promise.props(o).then(function(v) { 54 | assert.deepEqual({ 55 | one: 1, 56 | two: 2, 57 | three: 3, 58 | }, v); 59 | }); 60 | }); 61 | 62 | specify('should resolve eventual properties', function() { 63 | var d1 = pending(); 64 | var d2 = pending(); 65 | var d3 = pending(); 66 | var o = { 67 | one: d1.promise, 68 | two: d2.promise, 69 | three: d3.promise, 70 | }; 71 | setTimeout(function() { 72 | d1.resolve(1); 73 | d2.resolve(2); 74 | d3.resolve(3); 75 | }, 13); 76 | return Promise.props(o).then(function(v) { 77 | assert.deepEqual({ 78 | one: 1, 79 | two: 2, 80 | three: 3, 81 | }, v); 82 | }); 83 | }); 84 | 85 | specify('should reject if any input promise rejects', function() { 86 | var o = { 87 | one: fulfilled(1), 88 | two: rejected(2), 89 | three: fulfilled(3), 90 | }; 91 | return Promise.props(o).then(assert.fail, function(v) { 92 | assert(v === 2); 93 | }); 94 | }); 95 | 96 | specify('should accept a promise for an object', function() { 97 | var o = { 98 | one: fulfilled(1), 99 | two: fulfilled(2), 100 | three: fulfilled(3), 101 | }; 102 | var d1 = pending(); 103 | setTimeout(function() { 104 | d1.resolve(o); 105 | }, 13); 106 | return Promise.props(d1.promise).then(function(v) { 107 | assert.deepEqual({ 108 | one: 1, 109 | two: 2, 110 | three: 3, 111 | }, v); 112 | }); 113 | }); 114 | 115 | specify('should reject a promise for a primitive', function() { 116 | var d1 = pending(); 117 | setTimeout(function() { 118 | d1.resolve('text'); 119 | }, 13); 120 | return Promise.props(d1.promise).then(assert.fail, function(e) { 121 | assert(e instanceof TypeError); 122 | }); 123 | }); 124 | 125 | specify('should accept thenables in properties', function() { 126 | var t1 = { then: function(cb) {cb(1);} }; 127 | var t2 = { then: function(cb) {cb(2);} }; 128 | var t3 = { then: function(cb) {cb(3);} }; 129 | var o = { 130 | one: t1, 131 | two: t2, 132 | three: t3, 133 | }; 134 | return Promise.props(o).then(function(v) { 135 | assert.deepEqual({ 136 | one: 1, 137 | two: 2, 138 | three: 3, 139 | }, v); 140 | }); 141 | }); 142 | 143 | specify('should accept a thenable for thenables in properties', function() { 144 | var o = { 145 | then: function(f) { 146 | f({ 147 | one: { 148 | then: function(cb) { 149 | cb(1); 150 | }, 151 | }, 152 | two: { 153 | then: function(cb) { 154 | cb(2); 155 | }, 156 | }, 157 | three: { 158 | then: function(cb) { 159 | cb(3); 160 | }, 161 | }, 162 | }); 163 | }, 164 | }; 165 | return Promise.props(o).then(function(v) { 166 | assert.deepEqual({ 167 | one: 1, 168 | two: 2, 169 | three: 3, 170 | }, v); 171 | }); 172 | }); 173 | 174 | // jscs: disable requireCapitalizedComments 175 | /* 176 | specify('sends { key, value } progress updates', function(done) { 177 | var deferred1 = Q.defer(); 178 | var deferred2 = Q.defer(); 179 | 180 | var progressValues = []; 181 | 182 | Q.delay(50).then(function () { 183 | deferred1.notify('a'); 184 | }); 185 | Q.delay(100).then(function () { 186 | deferred2.notify('b'); 187 | deferred2.resolve(); 188 | }); 189 | Q.delay(150).then(function () { 190 | deferred1.notify('c'); 191 | deferred1.resolve(); 192 | }); 193 | 194 | Promise.props({ 195 | one: deferred1.promise, 196 | two: deferred2.promise 197 | }).then(function () { 198 | assert.deepEqual(progressValues, [ 199 | { key: 'one', value: 'a' }, 200 | { key: 'two', value: 'b' }, 201 | { key: 'one', value: 'c' } 202 | ]); 203 | done(); 204 | }, 205 | undefined, 206 | function (progressValue) { 207 | progressValues.push(progressValue); 208 | }); 209 | }); 210 | */ 211 | // jscs: enable requireCapitalizedComments 212 | 213 | specify('treats arrays for their properties', function() { 214 | var o = [1, 2, 3]; 215 | 216 | return Promise.props(o).then(function(v) { 217 | assert.deepEqual({ 218 | 0: 1, 219 | 1: 2, 220 | 2: 3, 221 | }, v); 222 | }); 223 | }); 224 | 225 | }); 226 | -------------------------------------------------------------------------------- /test/q_fin.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | /*! 8 | * 9 | Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to 12 | deal in the Software without restriction, including without limitation the 13 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 14 | sell copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 26 | IN THE SOFTWARE. 27 | */ 28 | 29 | describe('Promise#finally', function() { 30 | 31 | var exception1 = new Error('boo!'); 32 | var exception2 = new TypeError('evil!'); 33 | 34 | describe('when nothing is passed', function() { 35 | it('should do nothing', function() { 36 | return Promise.resolve('foo') 37 | ['finally']() 38 | ['finally']() 39 | ['finally']() 40 | ['finally']() 41 | .then(function(val) { 42 | assert(val === 'foo'); 43 | }); 44 | }); 45 | }); 46 | 47 | describe('when the promise is fulfilled', function() { 48 | 49 | it('should call the callback', function() { 50 | var called = false; 51 | 52 | return Promise.resolve('foo') 53 | ['finally'](function() { 54 | called = true; 55 | }) 56 | .then(function() { 57 | assert.equal(called, true); 58 | }); 59 | }); 60 | 61 | it('should fulfill with the original value', function() { 62 | return Promise.resolve('foo') 63 | ['finally'](function() { 64 | return 'bar'; 65 | }) 66 | .then(function(result) { 67 | assert.equal(result, 'foo'); 68 | }); 69 | }); 70 | 71 | describe('when the callback returns a promise', function() { 72 | 73 | describe('that is fulfilled', function() { 74 | it('should fulfill with the original reason after that promise resolves', function() { 75 | var pending = true; 76 | var promise = Promise.delay(null, 25).then(function() { 77 | pending = false; 78 | }); 79 | 80 | return Promise.resolve('foo') 81 | ['finally'](function() { 82 | return promise; 83 | }) 84 | .then(function(result) { 85 | assert.equal(pending, false); 86 | assert.equal(result, 'foo'); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('that is rejected', function() { 92 | it('should reject with this new rejection reason', function() { 93 | return Promise.resolve('foo') 94 | ['finally'](function() { 95 | return Promise.reject(exception1); 96 | }) 97 | .then(function() { 98 | assert.fail(); 99 | }, function(exception) { 100 | assert.equal(exception, exception1); 101 | }); 102 | }); 103 | }); 104 | 105 | }); 106 | 107 | describe('when the callback throws an exception', function() { 108 | it('should reject with this new exception', function() { 109 | return Promise.resolve('foo') 110 | ['finally'](function() { 111 | throw exception1; 112 | }) 113 | .then(function() { 114 | assert.fail(); 115 | }, function(exception) { 116 | assert.equal(exception, exception1); 117 | }); 118 | }); 119 | }); 120 | 121 | }); 122 | 123 | describe('when the promise is rejected', function() { 124 | 125 | it('should call the callback', function() { 126 | var called = false; 127 | 128 | return Promise.reject(exception1) 129 | ['finally'](function() { 130 | called = true; 131 | }) 132 | .then(function() { 133 | assert.fail(); 134 | }, function() { 135 | assert.equal(called, true); 136 | }); 137 | }); 138 | 139 | it('should reject with the original reason', function() { 140 | return Promise.reject(exception1) 141 | ['finally'](function() { 142 | return 'bar'; 143 | }) 144 | .then(function() { 145 | assert.fail(); 146 | }, function(exception) { 147 | assert.equal(exception, exception1); 148 | }); 149 | }); 150 | 151 | describe('when the callback returns a promise', function() { 152 | 153 | describe('that is fulfilled', function() { 154 | it('should reject with the original reason after that promise resolves', function() { 155 | var pending = true; 156 | var promise = Promise.delay(null, 25).then(function() { 157 | pending = false; 158 | }); 159 | 160 | return Promise.reject(exception1) 161 | ['finally'](function() { 162 | return promise; 163 | }).then(function() { 164 | assert.fail(); 165 | }, function(exception) { 166 | assert.equal(pending, false); 167 | assert.equal(exception, exception1); 168 | }); 169 | }); 170 | }); 171 | 172 | describe('that is rejected', function() { 173 | it('should reject with the new reason', function() { 174 | return Promise.reject(exception1) 175 | ['finally'](function() { 176 | return Promise.reject(exception2); 177 | }).then(function() { 178 | assert.fail(); 179 | }, function(exception) { 180 | assert.equal(exception, exception2); 181 | }); 182 | }); 183 | }); 184 | }); 185 | 186 | describe('when the callback throws an exception', function() { 187 | it('should reject with this new exception', function() { 188 | return Promise.reject(exception1) 189 | ['finally'](function() { 190 | throw exception2; 191 | }) 192 | .then(function() { 193 | assert.fail(); 194 | }, function(exception) { 195 | assert.equal(exception, exception2); 196 | }); 197 | }); 198 | }); 199 | }); 200 | 201 | describe('when the callback returns a thenable', function() { 202 | 203 | describe('that will fulfill', function() { 204 | it('should reject with the original reason after that', function() { 205 | var promise = { 206 | then: function(fn) { 207 | setTimeout(function() { 208 | fn(15); 209 | }, 13); 210 | }, 211 | }; 212 | 213 | return Promise.reject(exception1) 214 | ['finally'](function() { 215 | return promise; 216 | }) 217 | .then(function() { 218 | assert.fail(); 219 | }, function(exception) { 220 | assert.equal(exception, exception1); 221 | }); 222 | }); 223 | }); 224 | 225 | describe('that is rejected', function() { 226 | it('should reject with the new reason', function() { 227 | var promise = { 228 | then: function(f, fn) { 229 | setTimeout(function() { 230 | fn(exception2); 231 | }, 13); 232 | }, 233 | }; 234 | 235 | return Promise.reject(exception1) 236 | ['finally'](function() { 237 | return promise; 238 | }) 239 | .then(function() { 240 | assert.fail(); 241 | }, function(exception) { 242 | assert.equal(exception, exception2); 243 | }); 244 | }); 245 | }); 246 | 247 | }); 248 | }); 249 | -------------------------------------------------------------------------------- /test/q_nodify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | /* 8 | Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to 11 | deal in the Software without restriction, including without limitation the 12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | sell copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | IN THE SOFTWARE. 26 | */ 27 | 28 | describe('Promise#nodify', function() { 29 | var mkspy = function(calls) { 30 | return function spy() { 31 | var args = Array.prototype.slice.call(arguments); 32 | args.unshift(this); 33 | calls.push(args); 34 | }; 35 | }; 36 | 37 | it('calls back with a resolution', function() { 38 | var calls = []; 39 | var spy = mkspy(calls); 40 | 41 | Promise.resolve(10).nodify(spy); 42 | 43 | return Promise.delay(null, 10).then(function() { 44 | assert.deepEqual(calls, [[undefined, null, 10]]); 45 | }); 46 | }); 47 | 48 | it('calls back with an error', function() { 49 | var calls = []; 50 | var spy = mkspy(calls); 51 | 52 | Promise.reject(10).nodify(spy); 53 | 54 | return Promise.delay(null, 10).then(function() { 55 | assert.deepEqual(calls, [[undefined, 10]]); 56 | }); 57 | }); 58 | 59 | it('forwards a promise', function() { 60 | return Promise.resolve(10).nodify().then(function(ten) { 61 | assert.deepEqual(ten, 10); 62 | }); 63 | }); 64 | 65 | }); 66 | 67 | 68 | // Should be the last test because it is ridiculously hard to test 69 | // if something throws in the node process 70 | 71 | var isNodeJS = typeof process !== 'undefined' && 72 | typeof process.execPath === 'string'; 73 | 74 | if (isNodeJS) { 75 | describe('nodify', function() { 76 | 77 | var h = []; 78 | 79 | function clearHandlers() { 80 | var originalException; 81 | while (true) { 82 | originalException = process.listeners('uncaughtException').pop(); 83 | if (!originalException) { break; } 84 | process.removeListener('uncaughtException', originalException); 85 | h.push(originalException); 86 | } 87 | } 88 | 89 | function clearHandlersNoRestore() { 90 | var originalException; 91 | while (true) { 92 | originalException = process.listeners('uncaughtException').pop(); 93 | if (!originalException) { break; } 94 | process.removeListener('uncaughtException', originalException); 95 | } 96 | } 97 | 98 | var e = new Error(); 99 | function thrower() { 100 | throw e; 101 | } 102 | 103 | it('throws normally in the node process if the function throws', function(done) { 104 | clearHandlers(); 105 | var promise = Promise.resolve(10); 106 | var turns = 0; 107 | process.nextTick(function() { 108 | turns++; 109 | }); 110 | promise.nodify(thrower); 111 | process.addListener('uncaughtException', function(err) { 112 | clearHandlersNoRestore(); 113 | assert(err === e); 114 | assert(turns === 1); 115 | done(); 116 | }); 117 | }); 118 | }); 119 | } 120 | -------------------------------------------------------------------------------- /test/race.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | var fulfilled = Promise.resolve.bind(Promise); 8 | var rejected = Promise.reject.bind(Promise); 9 | var pending = Promise.defer.bind(Promise); 10 | 11 | describe('Promise.race', function() { 12 | 13 | it('remains forever pending when passed an empty array', function(done) { 14 | var p = Promise.race([]); 15 | var pending = true; 16 | var cb = function() { 17 | pending = false; 18 | done(new Error('should not reach here')); 19 | }; 20 | 21 | p.then(cb, cb).done(); 22 | 23 | setTimeout(function() { 24 | assert(pending); 25 | done(); 26 | }, 100); 27 | }); 28 | 29 | it('remains forever pending when passed a promise of an empty array', function(done) { 30 | var p = fulfilled([]).race(); 31 | var pending = true; 32 | var cb = function() { 33 | pending = false; 34 | done(new Error('should not reach here')); 35 | }; 36 | 37 | p.then(cb, cb).done(); 38 | 39 | setTimeout(function() { 40 | assert(pending); 41 | done(); 42 | }, 100); 43 | }); 44 | 45 | it('fulfills when passed an immediate value', function() { 46 | return Promise.race([1, 2, 3]).then(function(v) { 47 | assert.deepEqual(v, 1); 48 | }); 49 | }); 50 | 51 | it('fulfills when passed a promise of an immediate value', function() { 52 | return fulfilled([1, 2, 3]).race().then(function(v) { 53 | assert.deepEqual(v, 1); 54 | }); 55 | }); 56 | 57 | it('fulfills when passed an immediately fulfilled value', function() { 58 | var d1 = pending(); 59 | d1.resolve(1); 60 | var p1 = d1.promise; 61 | 62 | var d2 = pending(); 63 | d2.resolve(2); 64 | var p2 = d2.promise; 65 | 66 | var d3 = pending(); 67 | d3.resolve(3); 68 | var p3 = d3.promise; 69 | 70 | return Promise.race([p1, p2, p3]).then(function(v) { 71 | assert.deepEqual(v, 1); 72 | }); 73 | }); 74 | 75 | it('fulfills when passed a promise of an immediately fulfilled value', function() { 76 | var d1 = pending(); 77 | d1.resolve(1); 78 | var p1 = d1.promise; 79 | 80 | var d2 = pending(); 81 | d2.resolve(2); 82 | var p2 = d2.promise; 83 | 84 | var d3 = pending(); 85 | d3.resolve(3); 86 | var p3 = d3.promise; 87 | 88 | return fulfilled([p1, p2, p3]).race().then(function(v) { 89 | assert.deepEqual(v, 1); 90 | }); 91 | }); 92 | 93 | it('fulfills when passed an eventually fulfilled value', function() { 94 | var d1 = pending(); 95 | var p1 = d1.promise; 96 | 97 | var d2 = pending(); 98 | var p2 = d2.promise; 99 | 100 | var d3 = pending(); 101 | var p3 = d3.promise; 102 | 103 | setTimeout(function() { 104 | d1.resolve(1); 105 | d2.resolve(2); 106 | d3.resolve(3); 107 | }, 13); 108 | 109 | return Promise.race([p1, p2, p3]).then(function(v) { 110 | assert.deepEqual(v, 1); 111 | }); 112 | }); 113 | 114 | it('fulfills when passed a promise of an eventually fulfilled value', function() { 115 | var d1 = pending(); 116 | var p1 = d1.promise; 117 | 118 | var d2 = pending(); 119 | var p2 = d2.promise; 120 | 121 | var d3 = pending(); 122 | var p3 = d3.promise; 123 | 124 | setTimeout(function() { 125 | d1.resolve(1); 126 | d2.resolve(2); 127 | d3.resolve(3); 128 | }, 13); 129 | 130 | return fulfilled([p1, p2, p3]).race().then(function(v) { 131 | assert.deepEqual(v, 1); 132 | }); 133 | }); 134 | 135 | it('rejects when passed an immediate value', function() { 136 | return Promise.race([rejected(1), 2, 3]).then(assert.fail, function(v) { 137 | assert.deepEqual(v, 1); 138 | }); 139 | }); 140 | 141 | it('rejects when passed a promise of an immediate value', function() { 142 | return fulfilled([rejected(1), 2, 3]).race().then(assert.fail, function(v) { 143 | assert.deepEqual(v, 1); 144 | }); 145 | }); 146 | 147 | it('rejects when passed an immediately rejected value', function() { 148 | var d1 = pending(); 149 | d1.reject(1); 150 | var p1 = d1.promise; 151 | 152 | var d2 = pending(); 153 | d2.resolve(2); 154 | var p2 = d2.promise; 155 | 156 | var d3 = pending(); 157 | d3.resolve(3); 158 | var p3 = d3.promise; 159 | 160 | /* jshint elision: true */ 161 | return Promise.race([p1, p2, , , p3]).then(assert.fail, function(v) { 162 | assert.deepEqual(v, 1); 163 | }); 164 | }); 165 | 166 | it('rejects when passed a promise of an immediately rejected value', function() { 167 | var d1 = pending(); 168 | d1.reject(1); 169 | var p1 = d1.promise; 170 | 171 | var d2 = pending(); 172 | d2.resolve(2); 173 | var p2 = d2.promise; 174 | 175 | var d3 = pending(); 176 | d3.resolve(3); 177 | var p3 = d3.promise; 178 | 179 | /* jshint elision: true */ 180 | return fulfilled([p1, p2, , , p3]).race().then(assert.fail, function(v) { 181 | assert.deepEqual(v, 1); 182 | }); 183 | }); 184 | 185 | it('rejects when passed an eventually rejected value', function() { 186 | var d1 = pending(); 187 | var p1 = d1.promise; 188 | 189 | var d2 = pending(); 190 | var p2 = d2.promise; 191 | 192 | var d3 = pending(); 193 | var p3 = d3.promise; 194 | 195 | setTimeout(function() { 196 | d1.reject(1); 197 | d2.resolve(2); 198 | d3.resolve(3); 199 | }, 13); 200 | 201 | return Promise.race([p1, p2, p3]).then(assert.fail, function(v) { 202 | assert.deepEqual(v, 1); 203 | }); 204 | }); 205 | 206 | it('rejects when passed a promise of an eventually rejected value', function() { 207 | var d1 = pending(); 208 | var p1 = d1.promise; 209 | 210 | var d2 = pending(); 211 | var p2 = d2.promise; 212 | 213 | var d3 = pending(); 214 | var p3 = d3.promise; 215 | 216 | setTimeout(function() { 217 | d1.reject(1); 218 | d2.resolve(2); 219 | d3.resolve(3); 220 | }, 13); 221 | 222 | return fulfilled([p1, p2, p3]).race().then(assert.fail, function(v) { 223 | assert.deepEqual(v, 1); 224 | }); 225 | }); 226 | 227 | it('rejects when passed a rejected promise', function() { 228 | return rejected([]).race().then(assert.fail, function(v) { 229 | assert.deepEqual(v, []); 230 | }); 231 | }); 232 | }); 233 | -------------------------------------------------------------------------------- /test/reduce.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('../'); 5 | 6 | function promised(val) { 7 | return Promise.delay(val, 4); 8 | } 9 | 10 | function thenabled(val) { 11 | return { 12 | then: function(f) { 13 | setTimeout(function() { 14 | f(val); 15 | }, 4); 16 | }, 17 | }; 18 | } 19 | 20 | describe('Promise.prototype.reduce', function() { 21 | 22 | 23 | it('should allow returning values', function() { 24 | var a = [promised(1), promised(2), promised(3)]; 25 | 26 | return Promise.reduce(a, function(total, a) { 27 | return total + a + 5; 28 | }, 0).then(function(total) { 29 | assert.equal(total, 1 + 5 + 2 + 5 + 3 + 5); 30 | }); 31 | }); 32 | 33 | it('should allow returning promises', function() { 34 | var a = [promised(1), promised(2), promised(3)]; 35 | 36 | return Promise.reduce(a, function(total, a) { 37 | return promised(5).then(function(b) { 38 | return total + a + b; 39 | }); 40 | }, 0).then(function(total) { 41 | assert.equal(total, 1 + 5 + 2 + 5 + 3 + 5); 42 | }); 43 | }); 44 | 45 | it('should allow returning thenables', function() { 46 | var b = [1, 2, 3]; 47 | var a = []; 48 | 49 | return Promise.reduce(b, function(total, cur) { 50 | a.push(cur); 51 | return thenabled(3); 52 | }, 0).then(function(total) { 53 | assert.equal(total, 3); 54 | assert.deepEqual(a, b); 55 | }); 56 | }); 57 | 58 | it('propagates error', function() { 59 | var a = [promised(1), promised(2), promised(3)]; 60 | var e = new Error('asd'); 61 | return Promise.reduce(a, function(total, a) { 62 | if (a > 2) { 63 | throw e; 64 | } 65 | return total + a + 5; 66 | }, 0).then(assert.fail, function(err) { 67 | assert.equal(err, e); 68 | }); 69 | }); 70 | }); 71 | 72 | describe('Promise.prototype.reduceRight', function() { 73 | 74 | 75 | it('should allow returning values', function() { 76 | var a = [promised(1), promised(2), promised(3)]; 77 | 78 | return Promise.reduceRight(a, function(total, a) { 79 | return total + a + 5; 80 | }, 0).then(function(total) { 81 | assert.equal(total, 1 + 5 + 2 + 5 + 3 + 5); 82 | }); 83 | }); 84 | 85 | it('should allow returning promises', function() { 86 | var a = [promised(1), promised(2), promised(3)]; 87 | 88 | return Promise.reduceRight(a, function(total, a) { 89 | return promised(5).then(function(b) { 90 | return total + a + b; 91 | }); 92 | }, 0).then(function(total) { 93 | assert.equal(total, 1 + 5 + 2 + 5 + 3 + 5); 94 | }); 95 | }); 96 | 97 | it('should allow returning thenables', function() { 98 | var b = [1, 2, 3]; 99 | var a = []; 100 | var br = [3, 2, 1]; 101 | 102 | return Promise.reduceRight(b, function(total, cur) { 103 | a.push(cur); 104 | return thenabled(3); 105 | }, 0).then(function(total) { 106 | assert.equal(total, 3); 107 | assert.deepEqual(a, br); 108 | }); 109 | }); 110 | 111 | it('propagates error', function() { 112 | var a = [promised(1), promised(2), promised(3)]; 113 | var e = new Error('asd'); 114 | return Promise.reduceRight(a, function(total, a) { 115 | if (a > 2) { 116 | throw e; 117 | } 118 | return total + a + 5; 119 | }, 0).then(assert.fail, function(err) { 120 | assert.equal(err, e); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/tap.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | describe('tap', function() { 8 | specify('passes through value', function(done) { 9 | Promise.resolve('test').tap(function() { 10 | return 3; 11 | }).then(function(value) { 12 | assert.equal(value, 'test'); 13 | done(); 14 | }); 15 | }); 16 | 17 | specify('passes through value after returned promise is fulfilled', function(done) { 18 | var async = false; 19 | Promise.resolve('test').tap(function() { 20 | return new Promise(function(r) { 21 | setTimeout(function() { 22 | async = true; 23 | r(3); 24 | }, 13); 25 | }); 26 | }).then(function(value) { 27 | assert(async); 28 | assert.equal(value, 'test'); 29 | done(); 30 | }); 31 | }); 32 | 33 | specify('is not called on rejected promise', function(done) { 34 | var called = false; 35 | Promise.reject('test').tap(function() { 36 | called = true; 37 | }).caught(function(value) { // eslint-disable-line no-unused-vars 38 | assert(!called); 39 | done(); 40 | }); 41 | }); 42 | 43 | specify('passes immediate rejection', function(done) { 44 | var err = new Error(); 45 | Promise.resolve('test').tap(function() { 46 | throw err; 47 | }).tap(assert.fail).caught(function(e) { 48 | assert(err === e); 49 | done(); 50 | }); 51 | }); 52 | 53 | specify('passes eventual rejection', function(done) { 54 | var err = new Error(); 55 | Promise.resolve('test').tap(function() { 56 | return new Promise(function(_, rej) { 57 | setTimeout(function() { 58 | rej(err); 59 | }, 13); 60 | }); 61 | }).tap(assert.fail).caught(function(e) { 62 | assert(err === e); 63 | done(); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/timers.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | /* 8 | Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved. 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to 11 | deal in the Software without restriction, including without limitation the 12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | sell copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | IN THE SOFTWARE. 26 | */ 27 | 28 | describe('timeout', function() { 29 | it('should do nothing if the promise fulfills quickly', function() { 30 | return Promise.delay(10).timeout(200); 31 | }); 32 | 33 | it('should do nothing if the promise rejects quickly', function() { 34 | var goodError = new Error('haha!'); 35 | return Promise.delay(10) 36 | .then(function() { 37 | throw goodError; 38 | }) 39 | .timeout(200) 40 | .then(assert.fail, function(error) { 41 | assert(error === goodError); 42 | }); 43 | }); 44 | 45 | it('should reject with a timeout error if the promise is too slow', function() { 46 | var caught = false; 47 | return Promise.delay(100) 48 | .timeout(10) 49 | .caught(Promise.TimeoutError, function() { 50 | caught = true; 51 | }).then(function() { 52 | assert(caught); 53 | }); 54 | }); 55 | 56 | it('should reject with a custom timeout error if the promise is too slow and msg was provided', function() { 57 | var caught = false; 58 | return Promise.delay(100) 59 | .timeout(10, 'custom') 60 | .caught(Promise.TimeoutError, function(e) { 61 | assert(/custom/i.test(e.message)); 62 | caught = true; 63 | }).then(function() { 64 | assert(caught); 65 | }); 66 | }); 67 | }); 68 | 69 | describe('delay', function() { 70 | it('should delay fulfillment', function(done) { 71 | var pending = true; 72 | Promise.delay(30)['finally'](function() { pending = false; }).done(); 73 | 74 | setTimeout(function() { 75 | assert(pending); 76 | setTimeout(function() { 77 | assert(!pending); 78 | done(); 79 | }, 30); 80 | }, 15); 81 | }); 82 | 83 | it('should not delay rejection', function() { 84 | var pending = true; 85 | Promise.reject(5).delay(50)['finally'](function() { pending = false; }) 86 | ['catch'](function() {}).done(); 87 | 88 | return Promise.delay(20).then(function() { 89 | assert(!pending); 90 | }); 91 | }); 92 | 93 | it('should treat a single argument as a time', function(done) { 94 | var pending = true; 95 | Promise.delay(60)['finally'](function() { pending = false; }).done(); 96 | 97 | setTimeout(function() { 98 | assert(pending); 99 | done(); 100 | }, 30); 101 | 102 | }); 103 | 104 | it('should treat two arguments as a value + a time', function() { 105 | var pending = true; 106 | var promise = 107 | Promise.delay('what', 40)['finally'](function() { pending = false; }); 108 | 109 | return Promise.delay(20).then(function() { 110 | assert(pending); 111 | }).then(function() { 112 | return promise; 113 | }).then(function(value) { 114 | assert(!pending); 115 | assert(value === 'what'); 116 | }); 117 | }); 118 | 119 | it('should delay after resolution', function() { 120 | var promise1 = Promise.delay('what', 20); 121 | var promise2 = promise1.delay(40); 122 | var pending1 = true; 123 | var pending2 = true; 124 | promise1 = promise1['finally'](function() { pending1 = false; }); 125 | promise2 = promise2['finally'](function() { pending2 = false; }); 126 | 127 | return Promise.delay(40).then(function() { 128 | assert(!pending1); 129 | assert(pending2); 130 | }).then(function() { 131 | return promise2; 132 | }).then(function(value) { 133 | assert(!pending2); 134 | assert(value === 'what'); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /test/try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Promise = require('../'); 5 | 6 | var fulfilled = Promise.resolve.bind(Promise); 7 | var pending = Promise.defer.bind(Promise); 8 | 9 | var obj = {}; 10 | var error = new Error(); 11 | var thrower = function() { 12 | throw error; 13 | }; 14 | 15 | var identity = function(val) { 16 | return val; 17 | }; 18 | 19 | var array = function() { 20 | return [].slice.call(arguments); 21 | }; 22 | 23 | var receiver = function() { 24 | return this; 25 | }; 26 | 27 | var tryy = Promise['try'].bind(Promise); 28 | 29 | describe('Promise.try', function() { 30 | specify('should reject when the function throws', function() { 31 | var async = false; 32 | var p = tryy(thrower).then(assert.fail, function(e) { 33 | assert(async); 34 | assert(e === error); 35 | }); 36 | async = true; 37 | return p; 38 | }); 39 | specify('should reject when the function is not a function', function() { 40 | var async = false; 41 | var p = tryy(null).then(assert.fail, function(e) { 42 | assert(async); 43 | assert(e instanceof TypeError); 44 | }); 45 | async = true; 46 | return p; 47 | }); 48 | specify('should call the function with the given receiver', function() { 49 | var async = false; 50 | var p = tryy(receiver, obj).then(function(val) { 51 | assert(async); 52 | assert(val === obj); 53 | }); 54 | async = true; 55 | return p; 56 | }); 57 | specify('should call the function with the given value', function() { 58 | var async = false; 59 | var p = tryy(identity, null, obj).then(function(val) { 60 | assert(async); 61 | assert(val === obj); 62 | }); 63 | async = true; 64 | return p; 65 | }); 66 | specify('should call the function with the given values', function() { 67 | var async = false; 68 | var p = tryy(array, null, 1, 2, 3).then(function(val) { 69 | assert(async); 70 | assert.deepEqual(val, [1, 2, 3]); 71 | }); 72 | async = true; 73 | return p; 74 | }); 75 | 76 | specify('should unwrap this and arguments', function() { 77 | var d = pending(); 78 | var THIS = {}; 79 | var p = tryy(function(v) { 80 | assert(this === THIS); 81 | assert(v === 42); 82 | }, d.promise, fulfilled(42) 83 | ); 84 | 85 | setTimeout(function() { 86 | d.resolve(THIS); 87 | }, 10); 88 | 89 | return p; 90 | }); 91 | 92 | specify('should unwrap returned promise', function() { 93 | var d = pending(); 94 | 95 | var p = tryy(function() { 96 | return d.promise; 97 | }).then(function(v) { 98 | assert(v === 3); 99 | }); 100 | 101 | setTimeout(function() { 102 | d.resolve(3); 103 | }, 13); 104 | 105 | return p; 106 | }); 107 | specify('should unwrap returned thenable', function() { 108 | 109 | return tryy(function() { 110 | return { 111 | then: function(f, v) { // eslint-disable-line no-unused-vars 112 | f(3); 113 | }, 114 | }; 115 | }).then(function(v) { 116 | assert(v === 3); 117 | }); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/utility.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | 4 | var assert = require('assert'); 5 | var Promise = require('../'); 6 | 7 | describe('Promise#call', function() { 8 | it('should call when given an object', function() { 9 | return Promise.resolve({ 10 | test: function() { return Promise.resolve(this.foo); }, 11 | foo: 42, 12 | }).call('test').then(function(v) { 13 | assert.deepEqual(v, 42); 14 | }); 15 | }); 16 | 17 | it('should call with arguments when given an object', function() { 18 | return Promise.resolve({ 19 | test: function(x, y) { return Promise.resolve(x + 2 * y + this.foo); }, 20 | foo: 42, 21 | }).call('test', 3, Promise.resolve(7)).then(function(v) { 22 | assert.deepEqual(v, 3 + 2 * 7 + 42); 23 | }); 24 | }); 25 | 26 | it('should reject when not given an object', function() { 27 | return Promise.resolve(null).call('test').then(assert.fail, function(v) { 28 | assert(v instanceof TypeError); 29 | }); 30 | }); 31 | 32 | it('should reject when given an object without the named function', function() { 33 | return Promise.resolve({}).call('test').then(assert.fail, function(v) { 34 | assert(v instanceof TypeError); 35 | }); 36 | }); 37 | 38 | it('should reject when given a rejected promise', function() { 39 | return Promise.reject(3).call('test').then(assert.fail, function(v) { 40 | assert.deepEqual(v, 3); 41 | }); 42 | }); 43 | }); 44 | 45 | describe('Promise#get', function() { 46 | it('should fetch when given an object', function() { 47 | return Promise.resolve({ 48 | test: 42, 49 | }).get('test').then(function(v) { 50 | assert.deepEqual(v, 42); 51 | }); 52 | }); 53 | 54 | it('should resolve when given an object with a promise', function() { 55 | return Promise.resolve({ 56 | test: Promise.resolve(42), 57 | }).get('test').then(function(v) { 58 | assert.deepEqual(v, 42); 59 | }); 60 | }); 61 | 62 | it('should reject when not given an object', function() { 63 | return Promise.resolve(null).get('test').then(assert.fail, function(v) { 64 | assert(v instanceof TypeError); 65 | }); 66 | }); 67 | 68 | it('should reject when given a rejected promise', function() { 69 | return Promise.reject(3).get('test').then(assert.fail, function(v) { 70 | assert.deepEqual(v, 3); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('Promise#return', function() { 76 | it('should return an immediate value', function() { 77 | return Promise.resolve(7)['return'](42).then(function(v) { 78 | assert.deepEqual(v, 42); 79 | }); 80 | }); 81 | 82 | it('should return a promise', function() { 83 | return Promise.resolve(7)['return'](Promise.resolve(42)).then(function(v) { 84 | assert.deepEqual(v, 42); 85 | }); 86 | }); 87 | 88 | it('should reject if promise rejects', function() { 89 | return Promise.reject(7)['return'](42).then(assert.fail, function(v) { 90 | assert.deepEqual(v, 7); 91 | }); 92 | }); 93 | 94 | it('should reject if returning a rejected promise', function() { 95 | return Promise.resolve(7)['return'](Promise.reject(42)).then(assert.fail, function(v) { 96 | assert.deepEqual(v, 42); 97 | }); 98 | }); 99 | }); 100 | 101 | describe('Promise#throw', function() { 102 | it('should throw an immediate value', function() { 103 | return Promise.resolve(7)['throw'](42).then(assert.fail, function(v) { 104 | assert.deepEqual(v, 42); 105 | }); 106 | }); 107 | 108 | it('should throw a promise', function() { 109 | return Promise.resolve(7)['throw'](Promise.resolve(42)).then(assert.fail, function(v) { 110 | assert.deepEqual(v, 42); 111 | }); 112 | }); 113 | 114 | it('should reject if promise rejects', function() { 115 | return Promise.reject(7)['throw'](42).then(assert.fail, function(v) { 116 | assert.deepEqual(v, 7); 117 | }); 118 | }); 119 | 120 | it('should reject if throwing a rejected promise', function() { 121 | return Promise.resolve(7)['throw'](Promise.reject(42)).then(assert.fail, function(v) { 122 | assert.deepEqual(v, 42); 123 | }); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /test/when_all.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | /* 4 | Based on When.js tests 5 | 6 | Open Source Initiative OSI - The MIT License 7 | 8 | http://www.opensource.org/licenses/mit-license.php 9 | 10 | Copyright (c) 2011 Brian Cavalier 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 30 | var assert = require('assert'); 31 | var Promise = require('../'); 32 | 33 | var when = Promise; 34 | var resolved = Promise.resolve.bind(Promise); 35 | var rejected = Promise.reject.bind(Promise); 36 | 37 | describe('when.all-test', function() { 38 | 39 | specify('should resolve empty input', function() { 40 | return when.all([]).then( 41 | function(result) { 42 | assert.deepEqual(result, []); 43 | } 44 | ); 45 | }); 46 | 47 | specify('should resolve promise of empty input', function() { 48 | return resolved([]).all().then( 49 | function(result) { 50 | assert.deepEqual(result, []); 51 | } 52 | ); 53 | }); 54 | 55 | specify('should resolve values array', function() { 56 | var input = [1, 2, 3]; 57 | return when.all(input).then( 58 | function(results) { 59 | assert.deepEqual(results, input); 60 | } 61 | ); 62 | }); 63 | 64 | specify('should resolve promise of values array', function() { 65 | var input = [1, 2, 3]; 66 | return resolved(input).all().then( 67 | function(results) { 68 | assert.deepEqual(results, input); 69 | } 70 | ); 71 | }); 72 | 73 | specify('should resolve promises array', function() { 74 | var input = [resolved(1), resolved(2), resolved(3)]; 75 | return when.all(input).then( 76 | function(results) { 77 | assert.deepEqual(results, [1, 2, 3]); 78 | } 79 | ); 80 | }); 81 | 82 | specify('should resolve promise of promises array', function() { 83 | var input = [resolved(1), resolved(2), resolved(3)]; 84 | return resolved(input).all().then( 85 | function(results) { 86 | assert.deepEqual(results, [1, 2, 3]); 87 | } 88 | ); 89 | }); 90 | 91 | specify('should resolve sparse array input', function() { 92 | /* jshint elision: true */ 93 | var input = [, 1, , 1, 1]; 94 | return when.all(input).then( 95 | function(results) { 96 | assert.deepEqual(JSON.stringify(results), JSON.stringify(input)); 97 | } 98 | ); 99 | }); 100 | 101 | specify('should resolve promise of sparse array input', function() { 102 | /* jshint elision: true */ 103 | var input = [, 1, , 1, 1]; 104 | return resolved(input).all().then( 105 | function(results) { 106 | assert.deepEqual(JSON.stringify(results), JSON.stringify(input)); 107 | } 108 | ); 109 | }); 110 | 111 | specify('should reject if any input promise rejects', function() { 112 | var input = [resolved(1), rejected(2), resolved(3)]; 113 | return when.all(input).then( 114 | function() { throw new Error('should not reach here'); }, 115 | function(failed) { 116 | assert.deepEqual(failed, 2); 117 | } 118 | ); 119 | }); 120 | 121 | specify('should reject if any input promise rejects (2)', function() { 122 | var input = [resolved(1), rejected(2), resolved(3)]; 123 | return resolved(input).all().then( 124 | function() { throw new Error('should not reach here'); }, 125 | function(failed) { 126 | assert.deepEqual(failed, 2); 127 | } 128 | ); 129 | }); 130 | 131 | specify('should reject if any input promise rejects (3)', function() { 132 | var input = [resolved(1), resolved(2), resolved(3)]; 133 | return rejected(input).all().then( 134 | function() { throw new Error('should not reach here'); }, 135 | function(failed) { 136 | assert.deepEqual(failed.length, 3); 137 | } 138 | ); 139 | }); 140 | 141 | specify('should accept a promise for an array', function() { 142 | var expected, input; 143 | 144 | expected = [1, 2, 3]; 145 | input = resolved(expected); 146 | 147 | return input.all().then( 148 | function(results) { 149 | assert.deepEqual(results, expected); 150 | } 151 | ); 152 | }); 153 | 154 | specify('should reject when input promise does not resolve to array', function() { 155 | var caught = false; 156 | return when.all(resolved(1)).caught(TypeError, function(e) { // eslint-disable-line no-unused-vars 157 | caught = true; 158 | }).then(function() { 159 | assert.deepEqual(caught, true); 160 | }); 161 | }); 162 | 163 | }); 164 | -------------------------------------------------------------------------------- /test/when_defer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | /* 4 | Based on When.js tests 5 | 6 | Open Source Initiative OSI - The MIT License 7 | 8 | http://www.opensource.org/licenses/mit-license.php 9 | 10 | Copyright (c) 2011 Brian Cavalier 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 30 | var assert = require('assert'); 31 | var Promise = require('../'); 32 | 33 | var when = Promise; 34 | 35 | var sentinel = {}; 36 | 37 | function fakeResolved(val) { 38 | return { 39 | then: function(callback) { 40 | return fakeResolved(callback ? callback(val) : val); 41 | }, 42 | }; 43 | } 44 | 45 | function fakeRejected(reason) { 46 | return { 47 | then: function(callback, errback) { 48 | return errback ? fakeResolved(errback(reason)) : fakeRejected(reason); 49 | }, 50 | }; 51 | } 52 | 53 | describe('Promise.defer', function() { 54 | 55 | 56 | specify('should fulfill with an immediate value', function() { 57 | var d = when.defer(); 58 | 59 | var p = d.promise.then( 60 | function(val) { 61 | assert.equal(val, sentinel); 62 | } 63 | ); 64 | 65 | d.resolve(sentinel); 66 | 67 | return p; 68 | }); 69 | 70 | specify('should fulfill with fulfilled promised', function() { 71 | var d = when.defer(); 72 | 73 | var p = d.promise.then( 74 | function(val) { 75 | assert.equal(val, sentinel); 76 | } 77 | ); 78 | 79 | d.resolve(fakeResolved(sentinel)); 80 | 81 | return p; 82 | }); 83 | 84 | specify('should reject with rejected promise', function() { 85 | var d = when.defer(); 86 | 87 | var p = d.promise.then( 88 | assert.fail, 89 | function(val) { 90 | assert.equal(val, sentinel); 91 | } 92 | ); 93 | 94 | d.resolve(fakeRejected(sentinel)); 95 | 96 | return p; 97 | }); 98 | 99 | specify('should return a promise for the resolution value', function() { 100 | var d = when.defer(); 101 | 102 | d.resolve(sentinel); 103 | return d.promise.then( 104 | function(returnedPromiseVal) { 105 | assert.deepEqual(returnedPromiseVal, sentinel); 106 | } 107 | ); 108 | }); 109 | 110 | specify('should return a promise for a promised resolution value', function() { 111 | var d = when.defer(); 112 | 113 | d.resolve(when.resolve(sentinel)); 114 | return d.promise.then( 115 | function(returnedPromiseVal) { 116 | assert.deepEqual(returnedPromiseVal, sentinel); 117 | } 118 | ); 119 | }); 120 | 121 | specify('should return a promise for a promised rejection value', function() { 122 | var d = when.defer(); 123 | 124 | // Both the returned promise, and the deferred's own promise should 125 | // be rejected with the same value 126 | d.resolve(when.reject(sentinel)); 127 | return d.promise.then( 128 | assert.fail, 129 | function(returnedPromiseVal) { 130 | assert.deepEqual(returnedPromiseVal, sentinel); 131 | } 132 | ); 133 | }); 134 | 135 | specify('should invoke newly added callback when already resolved', function() { 136 | var d = when.defer(); 137 | 138 | d.resolve(sentinel); 139 | 140 | return d.promise.then( 141 | function(val) { 142 | assert.equal(val, sentinel); 143 | } 144 | ); 145 | }); 146 | 147 | 148 | 149 | specify('should reject with an immediate value', function() { 150 | var d = when.defer(); 151 | 152 | var p = d.promise.then( 153 | assert.fail, 154 | function(val) { 155 | assert.equal(val, sentinel); 156 | } 157 | ); 158 | 159 | d.reject(sentinel); 160 | 161 | return p; 162 | }); 163 | 164 | specify('should reject with fulfilled promised', function() { 165 | var d, expected; 166 | 167 | d = when.defer(); 168 | expected = fakeResolved(sentinel); 169 | 170 | var p = d.promise.then( 171 | assert.fail, 172 | function(val) { 173 | assert.equal(val, expected); 174 | } 175 | ); 176 | 177 | d.reject(expected); 178 | 179 | return p; 180 | }); 181 | 182 | specify('should reject with rejected promise', function() { 183 | var d, expected; 184 | 185 | d = when.defer(); 186 | expected = fakeRejected(sentinel); 187 | 188 | var p = d.promise.then( 189 | assert.fail, 190 | function(val) { 191 | assert.equal(val, expected); 192 | } 193 | ); 194 | 195 | d.reject(expected); 196 | 197 | return p; 198 | }); 199 | 200 | 201 | specify('should return a promise for the rejection value', function() { 202 | var d = when.defer(); 203 | 204 | // Both the returned promise, and the deferred's own promise should 205 | // be rejected with the same value 206 | d.reject(sentinel); 207 | return d.promise.then( 208 | assert.fail, 209 | function(returnedPromiseVal) { 210 | assert.deepEqual(returnedPromiseVal, sentinel); 211 | } 212 | ); 213 | }); 214 | 215 | specify('should invoke newly added errback when already rejected', function() { 216 | var d = when.defer(); 217 | 218 | d.reject(sentinel); 219 | 220 | return d.promise.then( 221 | assert.fail, 222 | function(val) { 223 | assert.deepEqual(val, sentinel); 224 | } 225 | ); 226 | }); 227 | }); 228 | -------------------------------------------------------------------------------- /test/when_join.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | Based on When.js tests 4 | 5 | Open Source Initiative OSI - The MIT License 6 | 7 | http://www.opensource.org/licenses/mit-license.php 8 | 9 | Copyright (c) 2011 Brian Cavalier 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining 12 | a copy of this software and associated documentation files (the 13 | "Software"), to deal in the Software without restriction, including 14 | without limitation the rights to use, copy, modify, merge, publish, 15 | distribute, sublicense, and/or sell copies of the Software, and to 16 | permit persons to whom the Software is furnished to do so, subject to 17 | the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 29 | var assert = require('assert'); 30 | var Promise = require('../'); 31 | 32 | var when = Promise; 33 | var resolved = Promise.resolve.bind(Promise); 34 | var rejected = Promise.reject.bind(Promise); 35 | 36 | describe('when.join-test', function() { 37 | 38 | specify('should resolve empty input', function() { 39 | return when.join().then( 40 | function(result) { 41 | assert.deepEqual(result, []); 42 | } 43 | ); 44 | }); 45 | 46 | specify('should join values', function() { 47 | return when.join(1, 2, 3).then( 48 | function(results) { 49 | assert.deepEqual(results, [1, 2, 3]); 50 | } 51 | ); 52 | }); 53 | 54 | specify('should join promises array', function() { 55 | return when.join(resolved(1), resolved(2), resolved(3)).then( 56 | function(results) { 57 | assert.deepEqual(results, [1, 2, 3]); 58 | } 59 | ); 60 | }); 61 | 62 | specify('should join mixed array', function() { 63 | return when.join(resolved(1), 2, resolved(3), 4).then( 64 | function(results) { 65 | assert.deepEqual(results, [1, 2, 3, 4]); 66 | } 67 | ); 68 | }); 69 | 70 | specify('should reject if any input promise rejects', function() { 71 | return when.join(resolved(1), rejected(2), resolved(3)).then( 72 | function() { throw new Error('should not reach here'); }, 73 | function(failed) { 74 | assert.deepEqual(failed, 2); 75 | } 76 | ); 77 | }); 78 | 79 | }); 80 | -------------------------------------------------------------------------------- /test/when_map.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | /* 4 | Based on When.js tests 5 | 6 | Open Source Initiative OSI - The MIT License 7 | 8 | http://www.opensource.org/licenses/mit-license.php 9 | 10 | Copyright (c) 2011 Brian Cavalier 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 30 | var assert = require('assert'); 31 | var Promise = require('../'); 32 | 33 | var when = Promise; 34 | var resolved = Promise.resolve.bind(Promise); 35 | var reject = Promise.reject.bind(Promise); 36 | 37 | var delay = Promise.delay.bind(Promise); 38 | 39 | describe('when.map-test', function() { 40 | 41 | function mapper(val) { 42 | return val * 2; 43 | } 44 | 45 | function deferredMapper(val) { 46 | return delay(mapper(val), Math.random() * 10); 47 | } 48 | 49 | specify('should map input values array', function() { 50 | var input = [1, 2, 3]; 51 | return when.map(input, mapper).then( 52 | function(results) { 53 | assert.deepEqual(results, [2, 4, 6]); 54 | } 55 | ); 56 | }); 57 | 58 | specify('should map input promises array', function() { 59 | var input = [resolved(1), resolved(2), resolved(3)]; 60 | return when.map(input, mapper).then( 61 | function(results) { 62 | assert.deepEqual(results, [2, 4, 6]); 63 | } 64 | ); 65 | }); 66 | 67 | specify('should map mixed input array', function() { 68 | var input = [1, resolved(2), 3]; 69 | return when.map(input, mapper).then( 70 | function(results) { 71 | assert.deepEqual(results, [2, 4, 6]); 72 | } 73 | ); 74 | }); 75 | 76 | specify('should map input when mapper returns a promise', function() { 77 | var input = [1, 2, 3]; 78 | return when.map(input, deferredMapper).then( 79 | function(results) { 80 | assert.deepEqual(results, [2, 4, 6]); 81 | } 82 | ); 83 | }); 84 | 85 | specify('should accept a promise for an array (1)', function() { 86 | return when.map(resolved([1, resolved(2), 3]), mapper).then( 87 | function(result) { 88 | assert.deepEqual(result, [2, 4, 6]); 89 | } 90 | ); 91 | }); 92 | 93 | specify('should accept a promise for an array (2)', function() { 94 | return resolved([1, resolved(2), 3]).map(mapper).then( 95 | function(result) { 96 | assert.deepEqual(result, [2, 4, 6]); 97 | } 98 | ); 99 | }); 100 | 101 | specify('should resolve to empty array when input promise does not resolve to an array (1)', function() { 102 | return when.map(resolved(123), mapper).then( 103 | function(result) { 104 | assert.deepEqual(result, []); 105 | } 106 | ); 107 | }); 108 | 109 | specify('should resolve to empty array when input promise does not resolve to an array (2)', function() { 110 | return resolved(123).map(mapper).then( 111 | function(result) { 112 | assert.deepEqual(result, []); 113 | } 114 | ); 115 | }); 116 | 117 | specify('should map input promises when mapper returns a promise', function() { 118 | var input = [resolved(1), resolved(2), resolved(3)]; 119 | return when.map(input, mapper).then( 120 | function(results) { 121 | assert.deepEqual(results, [2, 4, 6]); 122 | } 123 | ); 124 | }); 125 | 126 | specify('should reject when input contains rejection', function() { 127 | var input = [resolved(1), reject(2), resolved(3)]; 128 | return when.map(input, mapper).then( 129 | function() { throw new Error('should not reach here'); }, 130 | function(result) { 131 | assert(result === 2); 132 | } 133 | ); 134 | }); 135 | 136 | }); 137 | -------------------------------------------------------------------------------- /test/when_reduce.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | 'use strict'; 3 | /* 4 | Based on When.js tests 5 | 6 | Open Source Initiative OSI - The MIT License 7 | 8 | http://www.opensource.org/licenses/mit-license.php 9 | 10 | Copyright (c) 2011 Brian Cavalier 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 30 | var assert = require('assert'); 31 | var Promise = require('../'); 32 | 33 | var when = Promise; 34 | var resolved = Promise.resolve.bind(Promise); 35 | var rejected = Promise.reject.bind(Promise); 36 | 37 | var delay = Promise.delay.bind(Promise); 38 | 39 | var sentinel = {}; 40 | 41 | describe('when.reduce-test', function() { 42 | 43 | function plus(sum, val) { 44 | return sum + val; 45 | } 46 | 47 | function later(val) { 48 | return delay(val, Math.random() * 10); 49 | } 50 | 51 | 52 | specify('should reduce values without initial value', function() { 53 | return when.reduce([1, 2, 3], plus).then( 54 | function(result) { 55 | assert.deepEqual(result, 6); 56 | } 57 | ); 58 | }); 59 | 60 | specify('should reduce values with initial value', function() { 61 | return when.reduce([1, 2, 3], plus, 1).then( 62 | function(result) { 63 | assert.deepEqual(result, 7); 64 | } 65 | ); 66 | }); 67 | 68 | specify('should reduce values with initial promise', function() { 69 | return when.reduce([1, 2, 3], plus, resolved(1)).then( 70 | function(result) { 71 | assert.deepEqual(result, 7); 72 | } 73 | ); 74 | }); 75 | 76 | specify('should reduce promised values without initial value', function() { 77 | var input = [resolved(1), resolved(2), resolved(3)]; 78 | return when.reduce(input, plus).then( 79 | function(result) { 80 | assert.deepEqual(result, 6); 81 | } 82 | ); 83 | }); 84 | 85 | specify('should reduce promised values with initial value', function() { 86 | var input = [resolved(1), resolved(2), resolved(3)]; 87 | return when.reduce(input, plus, 1).then( 88 | function(result) { 89 | assert.deepEqual(result, 7); 90 | } 91 | ); 92 | }); 93 | 94 | specify('should reduce promised values with initial promise', function() { 95 | var input = [resolved(1), resolved(2), resolved(3)]; 96 | return when.reduce(input, plus, resolved(1)).then( 97 | function(result) { 98 | assert.deepEqual(result, 7); 99 | } 100 | ); 101 | }); 102 | 103 | specify('should reduce empty input with initial value', function() { 104 | var input = []; 105 | return when.reduce(input, plus, 1).then( 106 | function(result) { 107 | assert.deepEqual(result, 1); 108 | } 109 | ); 110 | }); 111 | 112 | specify('should reduce empty input with initial promise', function() { 113 | return when.reduce([], plus, resolved(1)).then( 114 | function(result) { 115 | assert.deepEqual(result, 1); 116 | } 117 | ); 118 | }); 119 | 120 | specify('should reject when input contains rejection', function() { 121 | var input = [resolved(1), rejected(2), resolved(3)]; 122 | return when.reduce(input, plus, resolved(1)).then( 123 | function() { throw new Error('should not reach here'); }, 124 | function(result) { 125 | assert.deepEqual(result, 2); 126 | } 127 | ); 128 | }); 129 | 130 | specify('should reject with empty array', function() { 131 | var caught = false; 132 | return when.reduce([], plus).caught(TypeError, function(e) { // eslint-disable-line no-unused-vars 133 | caught = true; 134 | }).then(function() { 135 | assert.deepEqual(caught, true); 136 | }); 137 | }); 138 | 139 | specify('should reduce to initial value with empty array', function() { 140 | return when.reduce([], plus, sentinel).then(function(r) { 141 | assert(r === sentinel); 142 | }); 143 | }); 144 | 145 | specify('should reduce in left-to-right order', function() { 146 | return when.reduce([later(1), later(2), later(3)], plus, '').then( 147 | function(result) { 148 | assert.deepEqual(result, '123'); 149 | } 150 | ); 151 | }); 152 | 153 | specify('should accept a promise for an array', function() { 154 | return when.reduce(resolved([1, 2, 3]), plus, '').then( 155 | function(result) { 156 | assert.deepEqual(result, '123'); 157 | } 158 | ); 159 | }); 160 | 161 | specify('should resolve to initialValue when input promise does not resolve to an array', function() { 162 | return when.reduce(resolved(123), plus, 1).then( 163 | function(result) { 164 | assert.deepEqual(result, 1); 165 | } 166 | ); 167 | }); 168 | 169 | specify('should provide correct basis value', function() { 170 | function insertIntoArray(arr, val, i) { 171 | arr[i] = val; 172 | return arr; 173 | } 174 | 175 | return when.reduce([later(1), later(2), later(3)], insertIntoArray, []).then( 176 | function(result) { 177 | assert.deepEqual(result, [1, 2, 3]); 178 | } 179 | ); 180 | }); 181 | }); 182 | 183 | describe('when.reduceRight-test', function() { 184 | 185 | function plus(sum, val) { 186 | return sum + val; 187 | } 188 | 189 | function later(val) { 190 | return delay(val, Math.random() * 10); 191 | } 192 | 193 | 194 | specify('should reduceRight values without initial value', function() { 195 | return when.reduceRight([1, 2, 3], plus).then( 196 | function(result) { 197 | assert.deepEqual(result, 6); 198 | } 199 | ); 200 | }); 201 | 202 | specify('should reduceRight values with initial value', function() { 203 | return when.reduceRight([1, 2, 3], plus, 1).then( 204 | function(result) { 205 | assert.deepEqual(result, 7); 206 | } 207 | ); 208 | }); 209 | 210 | specify('should reduceRight values with initial promise', function() { 211 | return when.reduceRight([1, 2, 3], plus, resolved(1)).then( 212 | function(result) { 213 | assert.deepEqual(result, 7); 214 | } 215 | ); 216 | }); 217 | 218 | specify('should reduceRight promised values without initial value', function() { 219 | var input = [resolved(1), resolved(2), resolved(3)]; 220 | return when.reduceRight(input, plus).then( 221 | function(result) { 222 | assert.deepEqual(result, 6); 223 | } 224 | ); 225 | }); 226 | 227 | specify('should reduceRight promised values with initial value', function() { 228 | var input = [resolved(1), resolved(2), resolved(3)]; 229 | return when.reduceRight(input, plus, 1).then( 230 | function(result) { 231 | assert.deepEqual(result, 7); 232 | } 233 | ); 234 | }); 235 | 236 | specify('should reduceRight promised values with initial promise', function() { 237 | var input = [resolved(1), resolved(2), resolved(3)]; 238 | return when.reduceRight(input, plus, resolved(1)).then( 239 | function(result) { 240 | assert.deepEqual(result, 7); 241 | } 242 | ); 243 | }); 244 | 245 | specify('should reduceRight empty input with initial value', function() { 246 | var input = []; 247 | return when.reduceRight(input, plus, 1).then( 248 | function(result) { 249 | assert.deepEqual(result, 1); 250 | } 251 | ); 252 | }); 253 | 254 | specify('should reduceRight empty input with initial promise', function() { 255 | return when.reduceRight([], plus, resolved(1)).then( 256 | function(result) { 257 | assert.deepEqual(result, 1); 258 | } 259 | ); 260 | }); 261 | 262 | specify('should reject when input contains rejection', function() { 263 | var input = [resolved(1), rejected(2), resolved(3)]; 264 | return when.reduceRight(input, plus, resolved(1)).then( 265 | function() { throw new Error('should not reach here'); }, 266 | function(result) { 267 | assert.deepEqual(result, 2); 268 | } 269 | ); 270 | }); 271 | 272 | specify('should reject with empty array', function() { 273 | var caught = false; 274 | return when.reduceRight([], plus).caught(TypeError, function(e) { // eslint-disable-line no-unused-vars 275 | caught = true; 276 | }).then(function() { 277 | assert.deepEqual(caught, true); 278 | }); 279 | }); 280 | 281 | specify('should reduceRight to initial value with empty array', function() { 282 | return when.reduceRight([], plus, sentinel).then(function(r) { 283 | assert(r === sentinel); 284 | }); 285 | }); 286 | 287 | specify('should reduceRight in right-to-left order', function() { 288 | return when.reduceRight([later(1), later(2), later(3)], plus, '').then( 289 | function(result) { 290 | assert.deepEqual(result, '321'); 291 | } 292 | ); 293 | }); 294 | 295 | specify('should accept a promise for an array', function() { 296 | return when.reduceRight(resolved([1, 2, 3]), plus, '').then( 297 | function(result) { 298 | assert.deepEqual(result, '321'); 299 | } 300 | ); 301 | }); 302 | 303 | specify('should resolve to initialValue when input promise does not resolve to an array', function() { 304 | return when.reduceRight(resolved(123), plus, 1).then( 305 | function(result) { 306 | assert.deepEqual(result, 1); 307 | } 308 | ); 309 | }); 310 | 311 | specify('should provide correct basis value', function() { 312 | function insertIntoArray(arr, val, i) { 313 | arr[i] = val; 314 | return arr; 315 | } 316 | 317 | return when.reduceRight([later(1), later(2), later(3)], insertIntoArray, []).then( 318 | function(result) { 319 | assert.deepEqual(result, [1, 2, 3]); 320 | } 321 | ); 322 | }); 323 | }); 324 | -------------------------------------------------------------------------------- /test/when_spread.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | Based on When.js tests 4 | 5 | Open Source Initiative OSI - The MIT License 6 | 7 | http://www.opensource.org/licenses/mit-license.php 8 | 9 | Copyright (c) 2011 Brian Cavalier 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining 12 | a copy of this software and associated documentation files (the 13 | "Software"), to deal in the Software without restriction, including 14 | without limitation the rights to use, copy, modify, merge, publish, 15 | distribute, sublicense, and/or sell copies of the Software, and to 16 | permit persons to whom the Software is furnished to do so, subject to 17 | the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 29 | var assert = require('assert'); 30 | var Promise = require('../'); 31 | 32 | var when = Promise; 33 | var resolved = Promise.resolve.bind(Promise); 34 | var rejected = Promise.reject.bind(Promise); // eslint-disable-line 35 | 36 | describe('when.spread-test', function() { 37 | var slice = [].slice; 38 | 39 | specify('should return a promise', function() { 40 | assert(typeof (resolved([1, 2]).spread().then) === 'function'); 41 | // Return a promise from all synchronous tests, for consistency 42 | return resolved(); 43 | }); 44 | 45 | specify('should apply onFulfilled with array as argument list', function() { 46 | var expected = [1, 2, 3]; 47 | return when.resolve(expected).spread(function() { 48 | assert.deepEqual(slice.call(arguments), expected); 49 | }); 50 | }); 51 | 52 | specify('should resolve array contents', function() { 53 | var expected = [when.resolve(1), 2, when.resolve(3)]; 54 | return when.resolve(expected).spread(function() { 55 | assert.deepEqual(slice.call(arguments), [1, 2, 3]); 56 | }); 57 | }); 58 | 59 | specify('should reject if any item in array rejects (1)', function() { 60 | var expected = [when.resolve(1), 2, when.reject(3)]; 61 | return when.resolve(expected) 62 | .spread(assert.fail).then(assert.fail, function(result) { 63 | assert.deepEqual(result, 3); 64 | }); 65 | }); 66 | 67 | specify('should reject if any item in array rejects (2)', function() { 68 | var expected = [when.resolve(1), 2, when.resolve(3)]; 69 | return when.reject(expected) 70 | .spread(assert.fail).then(assert.fail, function(result) { 71 | assert.deepEqual(result.length, 3); 72 | }); 73 | }); 74 | 75 | specify('should apply onFulfilled with array as argument list', function() { 76 | var expected = [1, 2, 3]; 77 | return when.resolve(when.resolve(expected)).spread(function() { 78 | assert.deepEqual(slice.call(arguments), expected); 79 | }); 80 | }); 81 | 82 | specify('should resolve array contents', function() { 83 | var expected = [when.resolve(1), 2, when.resolve(3)]; 84 | return when.resolve(when.resolve(expected)).spread(function() { 85 | assert.deepEqual(slice.call(arguments), [1, 2, 3]); 86 | }); 87 | }); 88 | 89 | specify('should resolve array contents in rejection', function() { 90 | var expected = [when.resolve(1), 2, when.resolve(3)]; 91 | return when.reject(expected).spread(assert.fail, function() { 92 | assert.deepEqual(slice.call(arguments), [1, 2, 3]); 93 | }); 94 | }); 95 | 96 | }); 97 | -------------------------------------------------------------------------------- /wrap.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); 2 | --------------------------------------------------------------------------------