├── .editorconfig ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── benchmark ├── index.html ├── map.js ├── promise.js └── run.js ├── bower.json ├── build ├── when.browserify-debug.js └── when.browserify.js ├── callbacks.js ├── cancelable.js ├── delay.js ├── docs ├── api.md ├── debug-api.md ├── es6-promise-shim.md └── installation.md ├── es6-shim ├── Promise.browserify-es6.js ├── Promise.js ├── Promise.js.map ├── Promise.min.js ├── Promise.min.js.map └── README.md ├── function.js ├── generator.js ├── guard.js ├── keys.js ├── lib ├── Promise.js ├── Scheduler.js ├── TimeoutError.js ├── apply.js ├── decorators │ ├── array.js │ ├── flow.js │ ├── fold.js │ ├── inspect.js │ ├── iterate.js │ ├── progress.js │ ├── timed.js │ ├── unhandledRejection.js │ └── with.js ├── env.js ├── format.js ├── liftAll.js ├── makePromise.js └── state.js ├── monitor.js ├── monitor ├── ConsoleReporter.js ├── PromiseMonitor.js ├── README.md ├── console.js └── error.js ├── node.js ├── node └── function.js ├── package.json ├── parallel.js ├── pipeline.js ├── poll.js ├── scripts ├── browserify-tests.js └── browserify.js ├── sequence.js ├── test ├── all-test.js ├── any-test.js ├── browser │ └── index.html ├── browsers.json ├── buster.js ├── callbacks-test.js ├── cancelable-test.js ├── cycle-test.js ├── defer-test.js ├── delay-test.js ├── else-test.js ├── filter-test.js ├── flow-test.js ├── fold-test.js ├── format-test.js ├── function-test.js ├── globalRejectionEvents-test.js ├── guard-test.js ├── inspect-test.js ├── isPromiseLike-test.js ├── iterate-test.js ├── join-test.js ├── keys-test.js ├── liftAll-test.js ├── map-test.js ├── monitor │ ├── PromiseMonitor-test.js │ ├── all.js │ ├── deep-chain.js │ ├── delay-handled.js │ ├── developer-error.js │ ├── done.js │ ├── index.html │ ├── lift-node.js │ ├── map.js │ ├── multiple-escapes.js │ ├── unhandled-begets-unhandled.js │ ├── unhandled-forever.js │ ├── unhandled-handled-later.js │ └── unhandledRejectionApi-test.js ├── node-test.js ├── parallel-test.js ├── pipeline-test.js ├── poll-test.js ├── promise-test.js ├── promises-aplus-adapter.js ├── race-test.js ├── reduce-test.js ├── reject-test.js ├── resolve-test.js ├── sauce.js ├── sequence-test.js ├── settle-test.js ├── some-test.js ├── timeout-test.js ├── unfold │ └── list-test.js ├── unhandledRejection-test.js ├── when-test.js └── with-test.js ├── timeout.js ├── unfold.js ├── unfold └── list.js └── when.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = LF 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | experiments/ 3 | node_modules/ 4 | build/when.js 5 | dist/ 6 | test/browser/*.js 7 | bower_components/ 8 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | .idea/ 4 | experiments/ 5 | test/monitor/ 6 | test/browser/ 7 | es6-shim/ 8 | build/when.js 9 | bower_components/ 10 | dist/ 11 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "node": true, 4 | 5 | "predef": [ 6 | "define", 7 | "module", 8 | "system" 9 | ], 10 | 11 | "boss": true, 12 | "curly": true, 13 | "elision": true, 14 | "eqnull": true, 15 | "expr": true, 16 | "globalstrict": false, 17 | "laxbreak": true, 18 | "newcap": true, 19 | "noarg": true, 20 | "noempty": true, 21 | "nonew": true, 22 | "quotmark": "single", 23 | "smarttabs": true, 24 | "strict": false, 25 | "sub": true, 26 | "trailing": true, 27 | "undef": true, 28 | "unused": true, 29 | 30 | "maxdepth": 3, 31 | "maxcomplexity": 5 32 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | benchmark/ 3 | experiments/ 4 | docs/ 5 | build/ 6 | *.md 7 | .* 8 | *~ 9 | bower.json 10 | bower_components/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.12' 5 | - '4' 6 | - '5' 7 | sudo: false 8 | script: npm run ci 9 | branches: 10 | only: 11 | - dev 12 | - master 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - g++-4.8 19 | env: 20 | global: 21 | - SAUCE_USERNAME="cujojs-when" 22 | - SAUCE_ACCESS_KEY="e5d3d1a5-bc49-4607-8a7e-702a512c7f60" 23 | - CXX=g++-4.8 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for helping out with when.js! We really appreciate you investing your time in the project. Below, find information and guides on the best way to contribute. 4 | 5 | Opening Issues 6 | -------------- 7 | 8 | No software is truly without bugs, and if you find one we would love it if you let us know so we can patch it up for you. When opening an issue, make sure to use a clear, short title along with a thorough description of the problem so we can best understand it. It's extremely helpful if you provide concrete steps on how to replicate the issue so that we can isolate it and figure it out more quickly. 9 | 10 | Pull Requests 11 | ------------- 12 | 13 | There's nothing better than a great pull request. To ensure that yours gets accepted as quickly and smoothly as possible, make sure the following steps have been taken: 14 | 15 | - Good clean commit messages. [This guide](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) should help. 16 | - A thorough description of what your code does in the description field 17 | - Tests written for any features that have been added. 18 | 19 | Running the Tests 20 | ----------------- 21 | 22 | #### Node 23 | 24 | Note that when.js includes the [Promises/A+ Test Suite](https://github.com/promises-aplus/promise-tests). Running unit tests in Node will run both when.js's own test suite, and the Promises/A+ Test Suite. 25 | 26 | 1. `npm install` 27 | 2. `npm test` 28 | 29 | #### Browsers 30 | 31 | 1. `npm install` 32 | 2. `npm run browser-test` 33 | 3. Point browsers at , e.g. `http://localhost:8080/` 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Open Source Initiative OSI - The MIT License 2 | 3 | http://www.opensource.org/licenses/mit-license.php 4 | 5 | Copyright (c) 2011 Brian Cavalier 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Promises/A+ logo 2 | 3 | [![Build Status](https://travis-ci.org/cujojs/when.svg?branch=master)](https://travis-ci.org/cujojs/when) 4 | [![Inline docs](http://inch-ci.org/github/cujojs/when.svg?branch=master)](http://inch-ci.org/github/cujojs/when) 5 | 6 | when.js 7 | ======= 8 | 9 | When.js is a rock solid, battle-tested [Promises/A+](http://promises-aplus.github.com/promises-spec) and `when()` implementation, including a complete [ES6 Promise shim](docs/es6-promise-shim.md). It's a powerful combination of small size, high performance, debuggability, and rich features: 10 | 11 | * Resolve arrays and hashes of promises, as well as infinite promise sequences 12 | * Execute tasks in parallel or sequentially 13 | * Transform Node-style and other callback-based APIs into promise-based APIs 14 | 15 | When.js is one of the many stand-alone components of [cujoJS](http://cujojs.com), the JavaScript Architectural Toolkit. 16 | 17 | Check it out: 18 | 19 | - [What's new](CHANGES.md) 20 | - [API docs](docs/api.md#api) 21 | - Read more about how [promises simplify async programming](http://know.cujojs.com/tutorials/async/simplifying-async-with-promises) 22 | 23 | Installation 24 | ------------ 25 | 26 | #### AMD 27 | 28 | Available as `when` through [bower](http://bower.io), or just clone the repo and load `when.js` from the root. 29 | 30 | ``` 31 | bower install --save when 32 | ``` 33 | 34 | #### CommonJS/Node 35 | 36 | ``` 37 | npm install --save when 38 | ``` 39 | 40 | [More help & other environments »](docs/installation.md) 41 | 42 | Usage 43 | ----- 44 | 45 | Promises can be used to help manage complex and/or nested callback flows in a simple manner. To get a better handle on how promise flows look and how they can be helpful, there are a couple examples below (using commonjs). 46 | 47 | This first example will print `"hello world!!!!"` if all went well, or `"drat!"` if there was a problem. It also uses [rest](https://github.com/cujojs/rest) to make an ajax request to a (fictional) external service. 48 | 49 | ```js 50 | var rest = require('rest'); 51 | 52 | fetchRemoteGreeting() 53 | .then(addExclamation) 54 | .catch(handleError) 55 | .done(function(greeting) { 56 | console.log(greeting); 57 | }); 58 | 59 | function fetchRemoteGreeting() { 60 | // convert native Promise to a when.js promise for 'hello world' 61 | var result = rest('http://example.com/greeting'); 62 | return when(result) 63 | } 64 | 65 | function addExclamation(greeting) { 66 | return greeting + '!!!!' 67 | } 68 | 69 | function handleError(e) { 70 | return 'drat!'; 71 | } 72 | ``` 73 | 74 | The second example shows off the power that comes with when's promise logic. Here, we get an array of numbers from a remote source and reduce them. The example will print `150` if all went well, and if there was a problem will print a full stack trace. 75 | 76 | ```js 77 | var when = require('when'); 78 | var rest = require('rest'); 79 | 80 | when.reduce(when.map(getRemoteNumberList(), times10), sum) 81 | .done(function(result) { 82 | console.log(result); 83 | }); 84 | 85 | function getRemoteNumberList() { 86 | // Get a remote array [1, 2, 3, 4, 5] 87 | return rest('http://example.com/numbers').then(JSON.parse); 88 | } 89 | 90 | function sum(x, y) { return x + y; } 91 | function times10(x) {return x * 10; } 92 | ``` 93 | 94 | License 95 | ------- 96 | 97 | Licensed under MIT. [Full license here »](LICENSE.txt) 98 | 99 | Contributing 100 | ------------ 101 | 102 | Please see the [contributing guide](CONTRIBUTING.md) for more information on running tests, opening issues, and contributing code to the project. 103 | 104 | References 105 | ---------- 106 | 107 | Much of this code was inspired by the async innards of [wire.js](https://github.com/cujojs/wire), and has been influenced by the great work in [Q](https://github.com/kriskowal/q), [Dojo's Deferred](https://github.com/dojo/dojo), and [uber.js](https://github.com/phiggins42/uber.js). 108 | -------------------------------------------------------------------------------- /benchmark/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /benchmark/map.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var when, tests, run; 9 | 10 | when = require('../when'); 11 | run = require('./run'); 12 | 13 | tests = [ 14 | { name: 'map 100', fn: map(100), defer: true }, 15 | { name: 'map 1k', fn: map(1e3), defer: true } 16 | ]; 17 | 18 | run(tests); 19 | 20 | // 21 | // Benchmark tests 22 | // 23 | 24 | function map(n) { 25 | return function(deferred) { 26 | 27 | var input = []; 28 | for(var i = 0; i < n; i++) { 29 | input.push(when(i)); 30 | } 31 | 32 | when.map(input, addOne).then(function() { 33 | deferred.resolve(); 34 | }); 35 | 36 | }; 37 | } 38 | 39 | // 40 | // Promise helpers 41 | // 42 | 43 | function addOne(x) { 44 | return x + 1; 45 | } 46 | 47 | }); 48 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 49 | -------------------------------------------------------------------------------- /benchmark/promise.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | /*global setImmediate,process*/ 8 | 9 | var Promise, tests, run, ok; 10 | 11 | Promise = require('../lib/Promise'); 12 | run = require('./run'); 13 | ok = 0; 14 | 15 | tests = [ 16 | { name: 'pending', fn: createPending }, 17 | { name: 'resolved', fn: createResolved }, 18 | { name: 'rejected', fn: createRejected }, 19 | { name: 'never', fn: createNever }, 20 | { name: 'resolve', fn: resolvePromise, defer: true }, 21 | { name: 'reject', fn: rejectPromise, defer: true }, 22 | { name: 'setImmediate', fn: viaSetImmediate, defer: true, 23 | condition: checkSetImmediate }, 24 | { name: 'process.nextTick', fn: viaProcessNextTick, defer: true, 25 | condition: checkProcessNextTick }, 26 | { name: 'setTimeout', fn: viaSetTimeout, defer: true }, 27 | { name: 'reject then resolve', fn: rejectThenResolve, defer: true }, 28 | { name: 'resolve chain 100', fn: resolveChain(100), defer: true }, 29 | { name: 'resolve chain 1k', fn: resolveChain(1e3), defer: true }, 30 | { name: 'sparse resolve chain 1k', fn: resolveChainSparse(1e3), defer: true }, 31 | { name: 'reject chain 100', fn: rejectChain(100), defer: true }, 32 | { name: 'reject chain 1k', fn: rejectChain(1e3), defer: true }, 33 | { name: 'sparse reject chain 1k', fn: rejectChainSparse(1e3), defer: true }, 34 | // These 10k tests seem to cause significant garbage collection 35 | // hits that skew results of other tests. So, they are disabled 36 | // for now, but we need to figure out how to reduce the memory 37 | // thrashing these cause. 38 | // Leaving one enabled for now. 39 | { name: 'resolve chain 10k', fn: resolveChain(1e4), defer: true } 40 | // { name: 'sparse resolve chain 10k', fn: resolveChainSparse(1e4), defer: true }, 41 | // { name: 'reject chain 10k', fn: rejectChain(1e4), defer: true }, 42 | // { name: 'sparse reject chain 10k', fn: rejectChainSparse(1e4), defer: true } 43 | ]; 44 | 45 | run(tests); 46 | 47 | // 48 | // Benchmark tests 49 | // 50 | 51 | function createPending() { 52 | /*jshint nonew:false*/ 53 | new Promise(pendingForever); 54 | } 55 | 56 | function createResolved() { 57 | Promise.resolve(); 58 | } 59 | 60 | function createRejected() { 61 | Promise.reject(); 62 | } 63 | 64 | function createNever() { 65 | Promise.never(); 66 | } 67 | 68 | function resolvePromise(deferred) { 69 | /*jshint nonew:false*/ 70 | new Promise(resolve).then(function() { 71 | deferred.resolve(); 72 | }); 73 | } 74 | 75 | function rejectPromise(deferred) { 76 | /*jshint nonew:false*/ 77 | new Promise(reject).then(null, function() { 78 | deferred.resolve(); 79 | }); 80 | } 81 | 82 | function rejectThenResolve(deferred) { 83 | /*jshint nonew:false*/ 84 | new Promise(reject).then(null, identity).then(function() { 85 | deferred.resolve(); 86 | }); 87 | } 88 | 89 | function viaSetTimeout(deferred) { 90 | setTimeout(function() { 91 | deferred.resolve(); 92 | }, 0); 93 | } 94 | 95 | function viaSetImmediate(deferred) { 96 | setImmediate(function() { 97 | deferred.resolve(); 98 | }); 99 | } 100 | 101 | function viaProcessNextTick(deferred) { 102 | process.nextTick(function() { 103 | deferred.resolve(); 104 | }); 105 | } 106 | 107 | function resolveChain(n) { 108 | return function(deferred) { 109 | var p = Promise.resolve({}), i = 0; 110 | for(;i < n; i++) { 111 | p = p.then(identity); 112 | } 113 | 114 | p.then(function() { 115 | deferred.resolve(); 116 | }); 117 | }; 118 | } 119 | 120 | function resolveChainSparse(n) { 121 | return function(deferred) { 122 | var p = Promise.resolve({}), i = 1; 123 | for(;i < n; i++) { 124 | p = p.then(null); 125 | } 126 | 127 | p.then(identity).then(function() { 128 | deferred.resolve(); 129 | }); 130 | }; 131 | } 132 | 133 | function rejectChain(n) { 134 | return function(deferred) { 135 | var p = Promise.reject({}), i = 0; 136 | for(;i < n; i++) { 137 | p = p.then(null, rethrow); 138 | } 139 | 140 | p.then(null, function() { 141 | deferred.resolve(); 142 | }); 143 | }; 144 | } 145 | 146 | function rejectChainSparse(n) { 147 | return function(deferred) { 148 | var p = Promise.reject({}), i = 1; 149 | for(;i < n; i++) { 150 | p = p.then(null, rethrow); 151 | } 152 | 153 | p.then(null, identity).then(function() { 154 | deferred.resolve(); 155 | }); 156 | }; 157 | } 158 | 159 | // 160 | // Promise helpers 161 | // 162 | 163 | function pendingForever() {} 164 | 165 | function resolve(r) { 166 | r(); 167 | } 168 | 169 | function reject(_, r) { 170 | r(); 171 | } 172 | 173 | function identity(x) { 174 | return x; 175 | } 176 | 177 | function rethrow(e) { 178 | throw e; 179 | } 180 | 181 | function checkSetImmediate() { 182 | return typeof setImmediate === 'function' 183 | ? ok : 'setImmediate() not available'; 184 | } 185 | 186 | function checkProcessNextTick() { 187 | return typeof process !== 'undefined' 188 | && typeof process.nextTick === 'function' 189 | ? ok : 'process.nextTick() not available'; 190 | } 191 | 192 | }); 193 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 194 | -------------------------------------------------------------------------------- /benchmark/run.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var Benchmark, log, err; 9 | 10 | Benchmark = require('benchmark'); 11 | log = console.log.bind(console); 12 | err = console.error.bind(console); 13 | 14 | // 15 | // Simple multi-benchmark runner 16 | // 17 | return function run(tests) { 18 | var skipped = []; 19 | 20 | tests.reduce(function (suite, test) { 21 | var skip = shouldSkip(test); 22 | if(skip) { 23 | skipped.push(pad(test.name, 24) + ' ' + skip); 24 | return suite; 25 | } 26 | 27 | test.onError = err; 28 | test.onComplete = function (event) { 29 | var result, t; 30 | 31 | t = event.currentTarget; 32 | 33 | result = pad(t.name, 24) 34 | + pad(t.hz.toFixed(2) + ' op/s', 18) 35 | + pad((1000 * t.stats.mean).toFixed(2), 8) 36 | + ' ms/op \xb1 ' + t.stats.rme.toFixed(2) + '%'; 37 | log(result); 38 | }; 39 | 40 | return suite.add(test); 41 | 42 | }, new Benchmark.Suite()) 43 | .on('start', function() { 44 | log('Platform:', Benchmark.platform.description || 'unknown'); 45 | if(skipped.length) { 46 | log('------------------------------------------------'); 47 | log('Skipping ' + count(skipped.length, 'benchmark')); 48 | log(skipped.join('\n')); 49 | } 50 | log('------------------------------------------------'); 51 | log('Running ' + count(this.length, 'benchmark')); 52 | }) 53 | .on('complete', function () { 54 | log('------------------------------------------------'); 55 | }).run(); 56 | 57 | }; 58 | 59 | function shouldSkip(test) { 60 | return typeof test.condition === 'function' 61 | && test.condition(); 62 | } 63 | 64 | function pad(str, len) { 65 | var result = str; 66 | while (result.length < len) { 67 | result = ' ' + result; 68 | } 69 | return result; 70 | } 71 | 72 | function count(n, s) { 73 | return '' + n + ' ' + (n === 1 ? s : (s + 's')); 74 | } 75 | 76 | }); 77 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 78 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "when", 3 | "main": "when.js", 4 | "moduleType": ["amd", "node"], 5 | "description": "A lightweight Promises/A+ and when() implementation, plus other async goodies.", 6 | "keywords": ["Promises/A+", "promises-aplus", "promise", "promises", "deferred", "deferreds", "when", "async", "asynchronous", "cujo"], 7 | "homepage": "https://github.com/cujojs/when", 8 | "authors": [ 9 | "Brian Cavalier " 10 | ], 11 | "license": "MIT", 12 | "ignore": [ 13 | "**/.*", 14 | "**/*.md", 15 | "docs", 16 | "benchmark", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "build" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /build/when.browserify-debug.js: -------------------------------------------------------------------------------- 1 | require('../monitor/console'); 2 | module.exports = require('./when.browserify.js'); 3 | 4 | -------------------------------------------------------------------------------- /build/when.browserify.js: -------------------------------------------------------------------------------- 1 | var when = module.exports = require('../when'); 2 | 3 | when.callbacks = require('../callbacks'); 4 | when.cancelable = require('../cancelable'); 5 | when.delay = require('../delay'); 6 | when.fn = require('../function'); 7 | when.guard = require('../guard'); 8 | when.keys = require('../keys'); 9 | when.nodefn = when.node = require('../node'); 10 | when.parallel = require('../parallel'); 11 | when.pipeline = require('../pipeline'); 12 | when.poll = require('../poll'); 13 | when.sequence = require('../sequence'); 14 | when.timeout = require('../timeout'); 15 | -------------------------------------------------------------------------------- /cancelable.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright B Cavalier & J Hann */ 2 | 3 | /** 4 | * cancelable.js 5 | * @deprecated 6 | * 7 | * Decorator that makes a deferred "cancelable". It adds a cancel() method that 8 | * will call a special cancel handler function and then reject the deferred. The 9 | * cancel handler can be used to do resource cleanup, or anything else that should 10 | * be done before any other rejection handlers are executed. 11 | * 12 | * Usage: 13 | * 14 | * var cancelableDeferred = cancelable(when.defer(), myCancelHandler); 15 | * 16 | * @author brian@hovercraftstudios.com 17 | */ 18 | 19 | (function(define) { 20 | define(function() { 21 | 22 | /** 23 | * Makes deferred cancelable, adding a cancel() method. 24 | * @deprecated 25 | * 26 | * @param deferred {Deferred} the {@link Deferred} to make cancelable 27 | * @param canceler {Function} cancel handler function to execute when this deferred 28 | * is canceled. This is guaranteed to run before all other rejection handlers. 29 | * The canceler will NOT be executed if the deferred is rejected in the standard 30 | * way, i.e. deferred.reject(). It ONLY executes if the deferred is canceled, 31 | * i.e. deferred.cancel() 32 | * 33 | * @returns deferred, with an added cancel() method. 34 | */ 35 | return function(deferred, canceler) { 36 | // Add a cancel method to the deferred to reject the delegate 37 | // with the special canceled indicator. 38 | deferred.cancel = function() { 39 | try { 40 | deferred.reject(canceler(deferred)); 41 | } catch(e) { 42 | deferred.reject(e); 43 | } 44 | 45 | return deferred.promise; 46 | }; 47 | 48 | return deferred; 49 | }; 50 | 51 | }); 52 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }); 53 | 54 | 55 | -------------------------------------------------------------------------------- /delay.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * delay.js 5 | * 6 | * Helper that returns a promise that resolves after a delay. 7 | * 8 | * @author Brian Cavalier 9 | * @author John Hann 10 | */ 11 | 12 | (function(define) { 13 | define(function(require) { 14 | 15 | var when = require('./when'); 16 | 17 | /** 18 | * @deprecated Use when(value).delay(ms) 19 | */ 20 | return function delay(msec, value) { 21 | return when(value).delay(msec); 22 | }; 23 | 24 | }); 25 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/debug-api.md: -------------------------------------------------------------------------------- 1 | # Debug APIs 2 | 3 | **NOTE:** This document describes APIs that provide support to external debugging tools. Do not use these APIs in normal application logic. For info and help on debugging promises in your application, see [Debugging Promises](api.md#debugging-promises). 4 | 5 | These APIs allow debugging tools to receive information about promise errors. For example, when.js's default [builtin unhandled rejection reporting](#debugging-promises) is built using these APIs, as is the long async stack trace support in [`when/monitor/console`](#whenmonitorconsole). 6 | 7 | A debugger could use them to do any number of things, such as display a list of "currently unhandled promise rejections", or send error reports to an error aggregation service. 8 | 9 | ## Global rejection events 10 | 11 | when.js >= 3.7.0 supports global `window` events in browsers, and `process` events in Node, similar to Node's `'uncaughtException'` event. This allows applications to register a handler to receive events from all promise implementations that support these global events. 12 | 13 | The events are: 14 | 15 | * `'unhandledRejection'`: fired when an unhandled rejection is detected 16 | * `'rejectionHandled'`: fired when rejection previously reported via an '`unhandledRejection'` event becomes handled 17 | 18 | ## Browser window events 19 | 20 | The following example shows how to use global `window` events in browsers to implement simple debug output. The `event` object has the following extra properties: 21 | 22 | * `event.detail.reason` - the rejection reason (typically an `Error` instance) 23 | * `event.detail.key` - opaque unique key representing the promise that was rejected. This key can be used to correlate corresponding `unhandledRejection` and `rejectionHandled` events for the same promise. 24 | 25 | ```js 26 | window.addEventListener('unhandledRejection', function(event) { 27 | // Calling preventDefault() suppresses when.js's default rejection logging 28 | // in favor of your own. 29 | event.preventDefault(); 30 | reportRejection(event.detail.reason, event.detail.key); 31 | }, false); 32 | 33 | window.addEventListener('rejectionHandled', function(event) { 34 | // Calling preventDefault() suppresses when.js's default rejection logging 35 | // in favor of your own. 36 | event.preventDefault(); 37 | reportHandled(event.detail.key); 38 | }, false); 39 | 40 | function reportRejection(error, key) { 41 | // Implement whatever logic your application requires 42 | // Log or record error state, etc. 43 | } 44 | 45 | function reportHandled(key) { 46 | // Implement whatever logic your application requires 47 | // Log that error has been handled, etc. 48 | } 49 | ``` 50 | 51 | ## Node global process events 52 | 53 | The following example shows how to use global `process` events in Node.js to implement simple debug output. The parameters passed to the `process` event handlers: 54 | 55 | * `reason` - the rejection reason (typically an `Error` instance) 56 | * `key` - opaque unique key representing the promise that was rejected. This key can be used to correlate corresponding `unhandledRejection` and `rejectionHandled` events for the same promise. 57 | 58 | 59 | ```js 60 | process.on('unhandledRejection', function(reason, key) { 61 | reportRejection(reason, key); 62 | }); 63 | 64 | process.on('rejectionHandled', function(key) { 65 | reportHandled(key); 66 | }); 67 | 68 | function reportRejection(error, key) { 69 | // Implement whatever logic your application requires 70 | // Log or record error state, etc. 71 | } 72 | 73 | function reportHandled(key) { 74 | // Implement whatever logic your application requires 75 | // Log that error has been handled, etc. 76 | } 77 | ``` 78 | 79 | ## Local when.js instance API 80 | 81 | ### Example 82 | 83 | A good example is the default implementation in `when/lib/decorators/unhandledRejection`, which logs unhandled rejections to the `console`. 84 | 85 | The following example shows how to combine `Promise.onPotentiallyUnhandledRejection` and `Promise.onPotentiallyUnhandledRejectionHandled` to implement a simple debug UI: 86 | 87 | ```js 88 | var Promise = require('when').Promise; 89 | 90 | Promise.onPotentiallyUnhandledRejection = function(rejection) { 91 | addToDebugDisplay(rejection.id, rejection.value); 92 | } 93 | 94 | Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) { 95 | removeFromDebugDisplay(rejection.id); 96 | } 97 | 98 | function addToDebugDisplay(id, error) { 99 | // Add to custom debug UI here 100 | } 101 | 102 | function removeFromDebugDisplay(id) { 103 | // Remove from custom debug UI here 104 | } 105 | ``` 106 | 107 | ### Promise.onPotentiallyUnhandledRejection 108 | 109 | Called for each rejected promise that appears not to have been handled. A rejection descriptor is provided as the argument: 110 | 111 | ``` 112 | { 113 | id: 114 | value: 115 | handled: 116 | // may contain other, internal/bookkeeping fields 117 | } 118 | ``` 119 | 120 | Technically, this is called after the internal task queue has been flushed, for each rejected promise that hasn't yet been *observed*, for example by calling its `.then` or `.catch` methods. 121 | 122 | The `handled` flag is useful if your implementation of `onPotentiallyUnhandledRejection` uses `setTimeout` or another technique to delay reporting, since the rejection *may be handled* between the instant you call `setTimeout` and the instant the timeout function executes. IOW, if you use `setTimeout`, you should check `handled` to verify that the rejection is still unhandled. 123 | 124 | The default implementation in `when/lib/decorators/unhandledRejection` logs error information to `console.error` that includes the `id`. 125 | 126 | ### Promise.onPotentiallyUnhandledRejectionHandled 127 | 128 | Called when a rejection previously passed to `Promise.onPotentiallyUnhandledRejection` becomes handled, for example if it is observed later by calling its `.then` or `.catch` methods. 129 | 130 | It is passed the same rejection descriptor object as `Promise.onPotentiallyUnhandledRejection`. The `handled` field will always be `true` when a descriptor is passed to `onPotentiallyUnhandledRejectionHandled`. 131 | 132 | The default implementation in `when/lib/decorators/unhandledRejection` logs an additional message to `console.log` with the `id` indicating that the rejection has been handled. 133 | 134 | ### Promise.onFatalRejection 135 | 136 | Called when an error propagates out of a terminal [`promise.done`](#promisedone), for example, if a callback passed to `promise.done` throws an exception. 137 | 138 | The default implementation in `when/lib/decorators/unhandledRejection` throws an uncatchable exception that will be logged to `console.error` by browsers and will halt (crash) Node.js. 139 | -------------------------------------------------------------------------------- /docs/es6-promise-shim.md: -------------------------------------------------------------------------------- 1 | # ES6 Promise shim 2 | 3 | When 3.x includes a shim for [ES6 Promise](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-constructor). To use it, simply load `when/es6-shim/Promise.js` via script tag or as a module. The shim will create a global `Promise` constructor. 4 | 5 | Since it's built on the when.js core, you get [debuggability with long stack traces](api.md#whenmonitorconsole), too! Just load `when/monitor/console` as usual. 6 | 7 | ## Global script 8 | 9 | ```html 10 | ` 73 | 1. Or `` for minified version 74 | 1. Or `` with [when/monitor/console](api.md#debugging-promises) enabled 75 | 1. `when` will be available as `window.when` 76 | 1. Other modules will be available as sub-objects/functions, e.g. `window.when.fn.lift`, `window.when.sequence`. See the [full sub-namespace list in the browserify build file](../build/when.browserify.js) 77 | 78 | If you expose the whole `dist/browser` folder in your application (or make sure that `when[.min|.debug].js` has its corresponding `*.map` file available next to it), you will have the [source maps](https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps) available for debugging in the browser. 79 | 80 | #### Web Worker (via browserify) 81 | 82 | Similarly to browser global environments: 83 | 84 | 1. `npm install --save when` 85 | 1. `importScripts('path/to/when/dist/browser/when.js');` 86 | 1. Or `importScripts('path/to/when/dist/browser/when.min.js');` for minified version 87 | 1. Or `importScripts('path/to/when/dist/browser/when.debug.js');` with [when/monitor/console](api.md#debugging-promises) enabled 88 | 1. `when` will be available as `self.when` 89 | 1. Other modules will be available as sub-objects/functions, e.g. `self.when.fn.lift`, `self.when.sequence`. See the [full sub-namespace list in the browserify build file](../build/when.browserify.js) 90 | -------------------------------------------------------------------------------- /es6-shim/Promise.browserify-es6.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | /** 6 | * ES6 global Promise shim 7 | */ 8 | var unhandledRejections = require('../lib/decorators/unhandledRejection'); 9 | var PromiseConstructor = unhandledRejections(require('../lib/Promise')); 10 | 11 | module.exports = typeof global != 'undefined' ? (global.Promise = PromiseConstructor) 12 | : typeof self != 'undefined' ? (self.Promise = PromiseConstructor) 13 | : PromiseConstructor; 14 | -------------------------------------------------------------------------------- /es6-shim/README.md: -------------------------------------------------------------------------------- 1 | # ES6 Promise shim 2 | 3 | Promise.js in this dir contains a complete ES6 Promise shim built on when.js that adds a global `Promise` in browser, AMD, Node, and other CommonJS environments. 4 | 5 | [Go to the full documentation](../docs/es6-promise-shim.md) -------------------------------------------------------------------------------- /function.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2013-2014 original author or authors */ 2 | 3 | /** 4 | * Collection of helper functions for wrapping and executing 'traditional' 5 | * synchronous functions in a promise interface. 6 | * 7 | * @author Brian Cavalier 8 | * @contributor Renato Zannon 9 | */ 10 | 11 | (function(define) { 12 | define(function(require) { 13 | 14 | var when = require('./when'); 15 | var attempt = when['try']; 16 | var _liftAll = require('./lib/liftAll'); 17 | var _apply = require('./lib/apply')(when.Promise); 18 | var slice = Array.prototype.slice; 19 | 20 | return { 21 | lift: lift, 22 | liftAll: liftAll, 23 | call: attempt, 24 | apply: apply, 25 | compose: compose 26 | }; 27 | 28 | /** 29 | * Takes a function and an optional array of arguments (that might be promises), 30 | * and calls the function. The return value is a promise whose resolution 31 | * depends on the value returned by the function. 32 | * @param {function} f function to be called 33 | * @param {Array} [args] array of arguments to func 34 | * @returns {Promise} promise for the return value of func 35 | */ 36 | function apply(f, args) { 37 | // slice args just in case the caller passed an Arguments instance 38 | return _apply(f, this, args == null ? [] : slice.call(args)); 39 | } 40 | 41 | /** 42 | * Takes a 'regular' function and returns a version of that function that 43 | * returns a promise instead of a plain value, and handles thrown errors by 44 | * returning a rejected promise. Also accepts a list of arguments to be 45 | * prepended to the new function, as does Function.prototype.bind. 46 | * 47 | * The resulting function is promise-aware, in the sense that it accepts 48 | * promise arguments, and waits for their resolution. 49 | * @param {Function} f function to be bound 50 | * @param {...*} [args] arguments to be prepended for the new function @deprecated 51 | * @returns {Function} a promise-returning function 52 | */ 53 | function lift(f /*, args... */) { 54 | var args = arguments.length > 1 ? slice.call(arguments, 1) : []; 55 | return function() { 56 | return _apply(f, this, args.concat(slice.call(arguments))); 57 | }; 58 | } 59 | 60 | /** 61 | * Lift all the functions/methods on src 62 | * @param {object|function} src source whose functions will be lifted 63 | * @param {function?} combine optional function for customizing the lifting 64 | * process. It is passed dst, the lifted function, and the property name of 65 | * the original function on src. 66 | * @param {(object|function)?} dst option destination host onto which to place lifted 67 | * functions. If not provided, liftAll returns a new object. 68 | * @returns {*} If dst is provided, returns dst with lifted functions as 69 | * properties. If dst not provided, returns a new object with lifted functions. 70 | */ 71 | function liftAll(src, combine, dst) { 72 | return _liftAll(lift, combine, dst, src); 73 | } 74 | 75 | /** 76 | * Composes multiple functions by piping their return values. It is 77 | * transparent to whether the functions return 'regular' values or promises: 78 | * the piped argument is always a resolved value. If one of the functions 79 | * throws or returns a rejected promise, the composed promise will be also 80 | * rejected. 81 | * 82 | * The arguments (or promises to arguments) given to the returned function (if 83 | * any), are passed directly to the first function on the 'pipeline'. 84 | * @param {Function} f the function to which the arguments will be passed 85 | * @param {...Function} [funcs] functions that will be composed, in order 86 | * @returns {Function} a promise-returning composition of the functions 87 | */ 88 | function compose(f /*, funcs... */) { 89 | var funcs = slice.call(arguments, 1); 90 | 91 | return function() { 92 | var thisArg = this; 93 | var args = slice.call(arguments); 94 | var firstPromise = attempt.apply(thisArg, [f].concat(args)); 95 | 96 | return when.reduce(funcs, function(arg, func) { 97 | return func.call(thisArg, arg); 98 | }, firstPromise); 99 | }; 100 | } 101 | }); 102 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 103 | 104 | 105 | -------------------------------------------------------------------------------- /generator.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var when = require('./when'); 9 | var slice = Array.prototype.slice; 10 | var Promise = when.Promise; 11 | var reject = Promise.reject; 12 | 13 | /** 14 | * Lift a generator to create a function that can suspend and 15 | * resume using the `yield` keyword to await promises. 16 | * @param {function} generator 17 | * @return {function} 18 | */ 19 | function lift(generator) { 20 | return function() { 21 | return run(generator, this, arguments); 22 | }; 23 | } 24 | 25 | /** 26 | * Immediately call a generator as a promise-aware coroutine 27 | * that can suspend and resume using the `yield` keyword to 28 | * await promises. Additional arguments after the first will 29 | * be passed through to the generator. 30 | * @param {function} generator 31 | * @returns {Promise} promise for the ultimate value returned 32 | * from the generator. 33 | */ 34 | function call(generator /*x, y, z...*/) { 35 | /*jshint validthis:true*/ 36 | return run(generator, this, slice.call(arguments, 1)); 37 | } 38 | 39 | /** 40 | * Immediately apply a generator, with the supplied args array, 41 | * as a promise-aware coroutine that can suspend and resume 42 | * using the `yield` keyword to await promises. 43 | * @param {function} generator 44 | * @param {Array} args arguments with which to initialize the generator 45 | * @returns {Promise} promise for the ultimate value returned 46 | * from the generator. 47 | */ 48 | function apply(generator, args) { 49 | /*jshint validthis:true*/ 50 | return run(generator, this, args || []); 51 | } 52 | 53 | /** 54 | * Helper to initiate the provided generator as a coroutine 55 | * @returns {*} 56 | */ 57 | function run(generator, thisArg, args) { 58 | return runNext(void 0, generator.apply(thisArg, args)); 59 | } 60 | 61 | function runNext(x, iterator) { 62 | try { 63 | return handle(iterator.next(x), iterator); 64 | } catch(e) { 65 | return reject(e); 66 | } 67 | } 68 | 69 | function next(x) { 70 | /*jshint validthis:true*/ 71 | return runNext(x, this); 72 | } 73 | 74 | function error(e) { 75 | /*jshint validthis:true*/ 76 | try { 77 | return handle(this.throw(e), this); 78 | } catch(e) { 79 | return reject(e); 80 | } 81 | } 82 | 83 | function handle(result, iterator) { 84 | if(result.done) { 85 | return result.value; 86 | } 87 | 88 | var h = Promise._handler(result.value); 89 | if(h.state() > 0) { 90 | return runNext(h.value, iterator); 91 | } 92 | 93 | var p = Promise._defer(); 94 | h.chain(p._handler, iterator, next, error); 95 | return p; 96 | } 97 | 98 | return { 99 | lift: lift, 100 | call: call, 101 | apply: apply 102 | }; 103 | 104 | }); 105 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 106 | -------------------------------------------------------------------------------- /guard.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * Generalized promise concurrency guard 5 | * Adapted from original concept by Sakari Jokinen (Rocket Pack, Ltd.) 6 | * 7 | * @author Brian Cavalier 8 | * @author John Hann 9 | * @contributor Sakari Jokinen 10 | */ 11 | (function(define) { 12 | define(function(require) { 13 | 14 | var when = require('./when'); 15 | var slice = Array.prototype.slice; 16 | 17 | guard.n = n; 18 | 19 | return guard; 20 | 21 | /** 22 | * Creates a guarded version of f that can only be entered when the supplied 23 | * condition allows. 24 | * @param {function} condition represents a critical section that may only 25 | * be entered when allowed by the condition 26 | * @param {function} f function to guard 27 | * @returns {function} guarded version of f 28 | */ 29 | function guard(condition, f) { 30 | return function() { 31 | var args = slice.call(arguments); 32 | 33 | return when(condition()).withThis(this).then(function(exit) { 34 | return when(f.apply(this, args))['finally'](exit); 35 | }); 36 | }; 37 | } 38 | 39 | /** 40 | * Creates a condition that allows only n simultaneous executions 41 | * of a guarded function 42 | * @param {number} allowed number of allowed simultaneous executions 43 | * @returns {function} condition function which returns a promise that 44 | * fulfills when the critical section may be entered. The fulfillment 45 | * value is a function ("notifyExit") that must be called when the critical 46 | * section has been exited. 47 | */ 48 | function n(allowed) { 49 | var count = 0; 50 | var waiting = []; 51 | 52 | return function enter() { 53 | return when.promise(function(resolve) { 54 | if(count < allowed) { 55 | resolve(exit); 56 | } else { 57 | waiting.push(resolve); 58 | } 59 | count += 1; 60 | }); 61 | }; 62 | 63 | function exit() { 64 | count = Math.max(count - 1, 0); 65 | if(waiting.length > 0) { 66 | waiting.shift()(exit); 67 | } 68 | } 69 | } 70 | 71 | }); 72 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 73 | -------------------------------------------------------------------------------- /keys.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author Brian Cavalier 8 | * @author John Hann 9 | */ 10 | (function(define) { 'use strict'; 11 | define(function(require) { 12 | 13 | var when = require('./when'); 14 | var Promise = when.Promise; 15 | var toPromise = when.resolve; 16 | 17 | return { 18 | all: when.lift(all), 19 | map: map, 20 | settle: settle 21 | }; 22 | 23 | /** 24 | * Resolve all the key-value pairs in the supplied object or promise 25 | * for an object. 26 | * @param {Promise|object} object or promise for object whose key-value pairs 27 | * will be resolved 28 | * @returns {Promise} promise for an object with the fully resolved key-value pairs 29 | */ 30 | function all(object) { 31 | var p = Promise._defer(); 32 | var resolver = Promise._handler(p); 33 | 34 | var results = {}; 35 | var keys = Object.keys(object); 36 | var pending = keys.length; 37 | 38 | for(var i=0, k; i= 0) { 57 | reported.splice(i, 1); 58 | logInfo('Handled previous rejection [' + r.id + '] ' + format.formatObject(r.value)); 59 | } 60 | } 61 | 62 | function enqueue(f, x) { 63 | tasks.push(f, x); 64 | if(running === null) { 65 | running = setTimer(flush, 0); 66 | } 67 | } 68 | 69 | function flush() { 70 | running = null; 71 | while(tasks.length > 0) { 72 | tasks.shift()(tasks.shift()); 73 | } 74 | } 75 | 76 | return Promise; 77 | }; 78 | 79 | function throwit(e) { 80 | throw e; 81 | } 82 | 83 | function noop() {} 84 | 85 | }); 86 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 87 | -------------------------------------------------------------------------------- /lib/decorators/with.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function() { 7 | 8 | return function addWith(Promise) { 9 | /** 10 | * Returns a promise whose handlers will be called with `this` set to 11 | * the supplied receiver. Subsequent promises derived from the 12 | * returned promise will also have their handlers called with receiver 13 | * as `this`. Calling `with` with undefined or no arguments will return 14 | * a promise whose handlers will again be called in the usual Promises/A+ 15 | * way (no `this`) thus safely undoing any previous `with` in the 16 | * promise chain. 17 | * 18 | * WARNING: Promises returned from `with`/`withThis` are NOT Promises/A+ 19 | * compliant, specifically violating 2.2.5 (http://promisesaplus.com/#point-41) 20 | * 21 | * @param {object} receiver `this` value for all handlers attached to 22 | * the returned promise. 23 | * @returns {Promise} 24 | */ 25 | Promise.prototype['with'] = Promise.prototype.withThis = function(receiver) { 26 | var p = this._beget(); 27 | var child = p._handler; 28 | child.receiver = receiver; 29 | this._handler.chain(child, receiver); 30 | return p; 31 | }; 32 | 33 | return Promise; 34 | }; 35 | 36 | }); 37 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 38 | 39 | -------------------------------------------------------------------------------- /lib/env.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | /*global process,document,setTimeout,clearTimeout,MutationObserver,WebKitMutationObserver*/ 6 | (function(define) { 'use strict'; 7 | define(function(require) { 8 | /*jshint maxcomplexity:6*/ 9 | 10 | // Sniff "best" async scheduling option 11 | // Prefer process.nextTick or MutationObserver, then check for 12 | // setTimeout, and finally vertx, since its the only env that doesn't 13 | // have setTimeout 14 | 15 | var MutationObs; 16 | var capturedSetTimeout = typeof setTimeout !== 'undefined' && setTimeout; 17 | 18 | // Default env 19 | var setTimer = function(f, ms) { return setTimeout(f, ms); }; 20 | var clearTimer = function(t) { return clearTimeout(t); }; 21 | var asap = function (f) { return capturedSetTimeout(f, 0); }; 22 | 23 | // Detect specific env 24 | if (isNode()) { // Node 25 | asap = function (f) { return process.nextTick(f); }; 26 | 27 | } else if (MutationObs = hasMutationObserver()) { // Modern browser 28 | asap = initMutationObserver(MutationObs); 29 | 30 | } else if (!capturedSetTimeout) { // vert.x 31 | var vertxRequire = require; 32 | var vertx = vertxRequire('vertx'); 33 | setTimer = function (f, ms) { return vertx.setTimer(ms, f); }; 34 | clearTimer = vertx.cancelTimer; 35 | asap = vertx.runOnLoop || vertx.runOnContext; 36 | } 37 | 38 | return { 39 | setTimer: setTimer, 40 | clearTimer: clearTimer, 41 | asap: asap 42 | }; 43 | 44 | function isNode () { 45 | return typeof process !== 'undefined' && 46 | Object.prototype.toString.call(process) === '[object process]'; 47 | } 48 | 49 | function hasMutationObserver () { 50 | return (typeof MutationObserver !== 'undefined' && MutationObserver) || 51 | (typeof WebKitMutationObserver !== 'undefined' && WebKitMutationObserver); 52 | } 53 | 54 | function initMutationObserver(MutationObserver) { 55 | var scheduled; 56 | var node = document.createTextNode(''); 57 | var o = new MutationObserver(run); 58 | o.observe(node, { characterData: true }); 59 | 60 | function run() { 61 | var f = scheduled; 62 | scheduled = void 0; 63 | f(); 64 | } 65 | 66 | var i = 0; 67 | return function (f) { 68 | scheduled = f; 69 | node.data = (i ^= 1); 70 | }; 71 | } 72 | }); 73 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 74 | -------------------------------------------------------------------------------- /lib/format.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function() { 7 | 8 | return { 9 | formatError: formatError, 10 | formatObject: formatObject, 11 | tryStringify: tryStringify 12 | }; 13 | 14 | /** 15 | * Format an error into a string. If e is an Error and has a stack property, 16 | * it's returned. Otherwise, e is formatted using formatObject, with a 17 | * warning added about e not being a proper Error. 18 | * @param {*} e 19 | * @returns {String} formatted string, suitable for output to developers 20 | */ 21 | function formatError(e) { 22 | var s = typeof e === 'object' && e !== null && (e.stack || e.message) ? e.stack || e.message : formatObject(e); 23 | return e instanceof Error ? s : s + ' (WARNING: non-Error used)'; 24 | } 25 | 26 | /** 27 | * Format an object, detecting "plain" objects and running them through 28 | * JSON.stringify if possible. 29 | * @param {Object} o 30 | * @returns {string} 31 | */ 32 | function formatObject(o) { 33 | var s = String(o); 34 | if(s === '[object Object]' && typeof JSON !== 'undefined') { 35 | s = tryStringify(o, s); 36 | } 37 | return s; 38 | } 39 | 40 | /** 41 | * Try to return the result of JSON.stringify(x). If that fails, return 42 | * defaultValue 43 | * @param {*} x 44 | * @param {*} defaultValue 45 | * @returns {String|*} JSON.stringify(x) or defaultValue 46 | */ 47 | function tryStringify(x, defaultValue) { 48 | try { 49 | return JSON.stringify(x); 50 | } catch(e) { 51 | return defaultValue; 52 | } 53 | } 54 | 55 | }); 56 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 57 | -------------------------------------------------------------------------------- /lib/liftAll.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function() { 7 | 8 | return function liftAll(liftOne, combine, dst, src) { 9 | if(typeof combine === 'undefined') { 10 | combine = defaultCombine; 11 | } 12 | 13 | return Object.keys(src).reduce(function(dst, key) { 14 | var f = src[key]; 15 | return typeof f === 'function' ? combine(dst, liftOne(f), key) : dst; 16 | }, typeof dst === 'undefined' ? defaultDst(src) : dst); 17 | }; 18 | 19 | function defaultCombine(o, f, k) { 20 | o[k] = f; 21 | return o; 22 | } 23 | 24 | function defaultDst(src) { 25 | return typeof src === 'function' ? src.bind() : Object.create(src); 26 | } 27 | }); 28 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 29 | -------------------------------------------------------------------------------- /lib/state.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function() { 7 | 8 | return { 9 | pending: toPendingState, 10 | fulfilled: toFulfilledState, 11 | rejected: toRejectedState, 12 | inspect: inspect 13 | }; 14 | 15 | function toPendingState() { 16 | return { state: 'pending' }; 17 | } 18 | 19 | function toRejectedState(e) { 20 | return { state: 'rejected', reason: e }; 21 | } 22 | 23 | function toFulfilledState(x) { 24 | return { state: 'fulfilled', value: x }; 25 | } 26 | 27 | function inspect(handler) { 28 | var state = handler.state(); 29 | return state === 0 ? toPendingState() 30 | : state > 0 ? toFulfilledState(handler.value) 31 | : toRejectedState(handler.value); 32 | } 33 | 34 | }); 35 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 36 | -------------------------------------------------------------------------------- /monitor.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var PromiseMonitor = require('./monitor/PromiseMonitor'); 9 | var ConsoleReporter = require('./monitor/ConsoleReporter'); 10 | 11 | var promiseMonitor = new PromiseMonitor(new ConsoleReporter()); 12 | 13 | return function(Promise) { 14 | return promiseMonitor.monitor(Promise); 15 | }; 16 | }); 17 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 18 | -------------------------------------------------------------------------------- /monitor/ConsoleReporter.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var error = require('./error'); 9 | var unhandledRejectionsMsg = '[promises] Unhandled rejections: '; 10 | var allHandledMsg = '[promises] All previously unhandled rejections have now been handled'; 11 | 12 | function ConsoleReporter() { 13 | this._previouslyReported = false; 14 | } 15 | 16 | ConsoleReporter.prototype = initDefaultLogging(); 17 | 18 | ConsoleReporter.prototype.log = function(traces) { 19 | if(traces.length === 0) { 20 | if(this._previouslyReported) { 21 | this._previouslyReported = false; 22 | this.msg(allHandledMsg); 23 | } 24 | return; 25 | } 26 | 27 | this._previouslyReported = true; 28 | this.groupStart(unhandledRejectionsMsg + traces.length); 29 | try { 30 | this._log(traces); 31 | } finally { 32 | this.groupEnd(); 33 | } 34 | }; 35 | 36 | ConsoleReporter.prototype._log = function(traces) { 37 | for(var i=0; i= 0; --i) { 78 | t = this._traces[i]; 79 | if(t.handler === handler) { 80 | break; 81 | } 82 | } 83 | 84 | if(i >= 0) { 85 | t.extraContext = extraContext; 86 | } else { 87 | this._traces.push({ 88 | handler: handler, 89 | extraContext: extraContext 90 | }); 91 | } 92 | 93 | this.logTraces(); 94 | }; 95 | 96 | PromiseMonitor.prototype.removeTrace = function(/*handler*/) { 97 | this.logTraces(); 98 | }; 99 | 100 | PromiseMonitor.prototype.fatal = function(handler, extraContext) { 101 | var err = new Error(); 102 | err.stack = this._createLongTrace(handler.value, handler.context, extraContext).join('\n'); 103 | setTimer(function() { 104 | throw err; 105 | }, 0); 106 | }; 107 | 108 | PromiseMonitor.prototype.logTraces = function() { 109 | if(!this._traceTask) { 110 | this._traceTask = setTimer(this._doLogTraces, this.logDelay); 111 | } 112 | }; 113 | 114 | PromiseMonitor.prototype._logTraces = function() { 115 | this._traceTask = void 0; 116 | this._traces = this._traces.filter(filterHandled); 117 | this._reporter.log(this.formatTraces(this._traces)); 118 | }; 119 | 120 | 121 | PromiseMonitor.prototype.formatTraces = function(traces) { 122 | return traces.map(function(t) { 123 | return this._createLongTrace(t.handler.value, t.handler.context, t.extraContext); 124 | }, this); 125 | }; 126 | 127 | PromiseMonitor.prototype._createLongTrace = function(e, context, extraContext) { 128 | var trace = error.parse(e) || [String(e) + ' (WARNING: non-Error used)']; 129 | trace = filterFrames(this.stackFilter, trace, 0); 130 | this._appendContext(trace, context); 131 | this._appendContext(trace, extraContext); 132 | return this.filterDuplicateFrames ? this._removeDuplicates(trace) : trace; 133 | }; 134 | 135 | PromiseMonitor.prototype._removeDuplicates = function(trace) { 136 | var seen = {}; 137 | var sep = this.stackJumpSeparator; 138 | var count = 0; 139 | return trace.reduceRight(function(deduped, line, i) { 140 | if(i === 0) { 141 | deduped.unshift(line); 142 | } else if(line === sep) { 143 | if(count > 0) { 144 | deduped.unshift(line); 145 | count = 0; 146 | } 147 | } else if(!seen[line]) { 148 | seen[line] = true; 149 | deduped.unshift(line); 150 | ++count; 151 | } 152 | return deduped; 153 | }, []); 154 | }; 155 | 156 | PromiseMonitor.prototype._appendContext = function(trace, context) { 157 | trace.push.apply(trace, this._createTrace(context)); 158 | }; 159 | 160 | PromiseMonitor.prototype._createTrace = function(traceChain) { 161 | var trace = []; 162 | var stack; 163 | 164 | while(traceChain) { 165 | stack = error.parse(traceChain); 166 | 167 | if (stack) { 168 | stack = filterFrames(this.stackFilter, stack); 169 | appendStack(trace, stack, this.stackJumpSeparator); 170 | } 171 | 172 | traceChain = traceChain.parent; 173 | } 174 | 175 | return trace; 176 | }; 177 | 178 | function appendStack(trace, stack, separator) { 179 | if (stack.length > 1) { 180 | stack[0] = separator; 181 | trace.push.apply(trace, stack); 182 | } 183 | } 184 | 185 | function filterFrames(stackFilter, stack) { 186 | return stack.filter(function(frame) { 187 | return !stackFilter.test(frame); 188 | }); 189 | } 190 | 191 | function filterHandled(t) { 192 | return !t.handler.handled; 193 | } 194 | 195 | return PromiseMonitor; 196 | }); 197 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 198 | -------------------------------------------------------------------------------- /monitor/README.md: -------------------------------------------------------------------------------- 1 | # Promise monitoring and debugging 2 | 3 | This dir contains experimental new promise monitoring and debugging utilities for when.js. See [the docs](../docs/api.md#debugging-promises). 4 | -------------------------------------------------------------------------------- /monitor/console.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function(require) { 7 | 8 | var monitor = require('../monitor'); 9 | var Promise = require('../when').Promise; 10 | 11 | return monitor(Promise); 12 | 13 | }); 14 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 15 | -------------------------------------------------------------------------------- /monitor/error.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | 5 | (function(define) { 'use strict'; 6 | define(function() { 7 | 8 | var parse, captureStack, format; 9 | 10 | if(Error.captureStackTrace) { 11 | // Use Error.captureStackTrace if available 12 | parse = function(e) { 13 | return e && e.stack && e.stack.split('\n'); 14 | }; 15 | 16 | format = formatAsString; 17 | captureStack = Error.captureStackTrace; 18 | 19 | } else { 20 | // Otherwise, do minimal feature detection to determine 21 | // how to capture and format reasonable stacks. 22 | parse = function(e) { 23 | var stack = e && e.stack && e.stack.split('\n'); 24 | if(stack && e.message) { 25 | stack.unshift(e.message); 26 | } 27 | return stack; 28 | }; 29 | 30 | (function() { 31 | var e = new Error(); 32 | if(typeof e.stack !== 'string') { 33 | format = formatAsString; 34 | captureStack = captureSpiderMonkeyStack; 35 | } else { 36 | format = formatAsErrorWithStack; 37 | captureStack = useStackDirectly; 38 | } 39 | }()); 40 | } 41 | 42 | function captureSpiderMonkeyStack(host) { 43 | try { 44 | throw new Error(); 45 | } catch(err) { 46 | host.stack = err.stack; 47 | } 48 | } 49 | 50 | function useStackDirectly(host) { 51 | host.stack = new Error().stack; 52 | } 53 | 54 | function formatAsString(longTrace) { 55 | return join(longTrace); 56 | } 57 | 58 | function formatAsErrorWithStack(longTrace) { 59 | var e = new Error(); 60 | e.stack = formatAsString(longTrace); 61 | return e; 62 | } 63 | 64 | // About 5-10x faster than String.prototype.join o_O 65 | function join(a) { 66 | var sep = false; 67 | var s = ''; 68 | for(var i=0; i< a.length; ++i) { 69 | if(sep) { 70 | s += '\n' + a[i]; 71 | } else { 72 | s+= a[i]; 73 | sep = true; 74 | } 75 | } 76 | return s; 77 | } 78 | 79 | return { 80 | parse: parse, 81 | format: format, 82 | captureStack: captureStack 83 | }; 84 | 85 | }); 86 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 87 | -------------------------------------------------------------------------------- /node/function.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2013 original author or authors */ 2 | 3 | /** 4 | * @author Brian Cavalier 5 | */ 6 | (function(define) { 'use strict'; 7 | define(function(require) { 8 | 9 | // DEPRECATED: Use when/node instead 10 | return require('../node'); 11 | 12 | }); 13 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "when", 3 | "version": "3.7.8", 4 | "description": "A lightweight Promises/A+ and when() implementation, plus other async goodies.", 5 | "keywords": [ 6 | "cujo", 7 | "Promises/A+", 8 | "promises-aplus", 9 | "promise", 10 | "promises", 11 | "deferred", 12 | "deferreds", 13 | "when", 14 | "async", 15 | "asynchronous", 16 | "ender" 17 | ], 18 | "homepage": "http://cujojs.com", 19 | "license": "MIT", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/cujojs/when" 23 | }, 24 | "bugs": "https://github.com/cujojs/when/issues", 25 | "maintainers": [ 26 | { 27 | "name": "Brian Cavalier", 28 | "web": "http://hovercraftstudios.com" 29 | }, 30 | { 31 | "name": "John Hann", 32 | "web": "http://unscriptable.com" 33 | } 34 | ], 35 | "contributors": [ 36 | { 37 | "name": "Brian Cavalier", 38 | "web": "http://hovercraftstudios.com" 39 | }, 40 | { 41 | "name": "John Hann", 42 | "web": "http://unscriptable.com" 43 | }, 44 | { 45 | "name": "Scott Andrews" 46 | } 47 | ], 48 | "devDependencies": { 49 | "benchmark": "~1", 50 | "browserify": "~2", 51 | "buster": "~0.7", 52 | "exorcist": "~0.4", 53 | "glob": "^7.1.1", 54 | "jshint": "~2", 55 | "json5": "~0.2", 56 | "microtime": "~2", 57 | "mkdirp": "^0.5.1", 58 | "optimist": "~0.6", 59 | "poly": "^0.6.1", 60 | "promises-aplus-tests": "~2", 61 | "rest": "1.1.x", 62 | "sauce-connect-launcher": "~0.4", 63 | "uglify-js": "~2", 64 | "wd": "~0.2" 65 | }, 66 | "main": "when.js", 67 | "ender": { 68 | "files": [ 69 | "*.js", 70 | "lib/*.js", 71 | "node/*.js", 72 | "unfold/*.js", 73 | "monitor/*.js", 74 | "lib/decorators/*.js" 75 | ] 76 | }, 77 | "browser": { 78 | "when": "./dist/browser/when.js", 79 | "vertx": false 80 | }, 81 | "directories": { 82 | "test": "test" 83 | }, 84 | "scripts": { 85 | "test": "jshint . && buster-test -e node && promises-aplus-tests test/promises-aplus-adapter.js", 86 | "build-browser-test": "browserify ./node_modules/poly/es5.js -o test/browser/es5.js && node scripts/browserify-tests", 87 | "browser-test": "npm run build-browser-test && buster-static -e browser -p 8080", 88 | "ci": "npm test && node test/sauce.js", 89 | "tunnel": "node test/sauce.js -m", 90 | "start": "buster-static -e browser", 91 | "benchmark": "node benchmark/promise && node benchmark/map", 92 | "prepublish": "npm run browserify && npm run uglify", 93 | "preversion": "npm run browserify && npm run uglify", 94 | "browserify": "npm run browserify-es6 && npm run browserify-when && npm run browserify-debug", 95 | "browserify-es6": "node scripts/browserify.js es6", 96 | "browserify-when": "node scripts/browserify.js when", 97 | "browserify-debug": "node scripts/browserify.js debug", 98 | "uglify": "npm run uglify-es6 && npm run uglify-when", 99 | "uglify-es6": "uglifyjs es6-shim/Promise.js --compress --mangle --in-source-map es6-shim/Promise.js.map --source-map es6-shim/Promise.min.js.map -o es6-shim/Promise.min.js", 100 | "uglify-when": "uglifyjs dist/browser/when.js --compress --mangle --in-source-map dist/browser/when.js.map --source-map dist/browser/when.min.js.map -o dist/browser/when.min.js" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /parallel.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * parallel.js 5 | * 6 | * Run a set of task functions in parallel. All tasks will 7 | * receive the same args 8 | * 9 | * @author Brian Cavalier 10 | * @author John Hann 11 | */ 12 | 13 | (function(define) { 14 | define(function(require) { 15 | 16 | var when = require('./when'); 17 | var all = when.Promise.all; 18 | var slice = Array.prototype.slice; 19 | 20 | /** 21 | * Run array of tasks in parallel 22 | * @param tasks {Array|Promise} array or promiseForArray of task functions 23 | * @param [args] {*} arguments to be passed to all tasks 24 | * @return {Promise} promise for array containing the 25 | * result of each task in the array position corresponding 26 | * to position of the task in the tasks array 27 | */ 28 | return function parallel(tasks /*, args... */) { 29 | return all(slice.call(arguments, 1)).then(function(args) { 30 | return when.map(tasks, function(task) { 31 | return task.apply(void 0, args); 32 | }); 33 | }); 34 | }; 35 | 36 | }); 37 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 38 | 39 | 40 | -------------------------------------------------------------------------------- /pipeline.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * pipeline.js 5 | * 6 | * Run a set of task functions in sequence, passing the result 7 | * of the previous as an argument to the next. Like a shell 8 | * pipeline, e.g. `cat file.txt | grep 'foo' | sed -e 's/foo/bar/g' 9 | * 10 | * @author Brian Cavalier 11 | * @author John Hann 12 | */ 13 | 14 | (function(define) { 15 | define(function(require) { 16 | 17 | var when = require('./when'); 18 | var all = when.Promise.all; 19 | var slice = Array.prototype.slice; 20 | 21 | /** 22 | * Run array of tasks in a pipeline where the next 23 | * tasks receives the result of the previous. The first task 24 | * will receive the initialArgs as its argument list. 25 | * @param tasks {Array|Promise} array or promise for array of task functions 26 | * @param [initialArgs...] {*} arguments to be passed to the first task 27 | * @return {Promise} promise for return value of the final task 28 | */ 29 | return function pipeline(tasks /* initialArgs... */) { 30 | // Self-optimizing function to run first task with multiple 31 | // args using apply, but subsequence tasks via direct invocation 32 | var runTask = function(args, task) { 33 | runTask = function(arg, task) { 34 | return task(arg); 35 | }; 36 | 37 | return task.apply(null, args); 38 | }; 39 | 40 | return all(slice.call(arguments, 1)).then(function(args) { 41 | return when.reduce(tasks, function(arg, task) { 42 | return runTask(arg, task); 43 | }, args); 44 | }); 45 | }; 46 | 47 | }); 48 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 49 | 50 | 51 | -------------------------------------------------------------------------------- /poll.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2012-2013 original author or authors */ 2 | 3 | /** 4 | * poll.js 5 | * 6 | * Helper that polls until cancelled or for a condition to become true. 7 | * 8 | * @author Scott Andrews 9 | */ 10 | 11 | (function (define) { 'use strict'; 12 | define(function(require) { 13 | 14 | var when = require('./when'); 15 | var attempt = when['try']; 16 | var cancelable = require('./cancelable'); 17 | 18 | /** 19 | * Periodically execute the task function on the msec delay. The result of 20 | * the task may be verified by watching for a condition to become true. The 21 | * returned deferred is cancellable if the polling needs to be cancelled 22 | * externally before reaching a resolved state. 23 | * 24 | * The next vote is scheduled after the results of the current vote are 25 | * verified and rejected. 26 | * 27 | * Polling may be terminated by the verifier returning a truthy value, 28 | * invoking cancel() on the returned promise, or the task function returning 29 | * a rejected promise. 30 | * 31 | * Usage: 32 | * 33 | * var count = 0; 34 | * function doSomething() { return count++ } 35 | * 36 | * // poll until cancelled 37 | * var p = poll(doSomething, 1000); 38 | * ... 39 | * p.cancel(); 40 | * 41 | * // poll until condition is met 42 | * poll(doSomething, 1000, function(result) { return result > 10 }) 43 | * .then(function(result) { assert result == 10 }); 44 | * 45 | * // delay first vote 46 | * poll(doSomething, 1000, anyFunc, true); 47 | * 48 | * @param task {Function} function that is executed after every timeout 49 | * @param interval {number|Function} timeout in milliseconds 50 | * @param [verifier] {Function} function to evaluate the result of the vote. 51 | * May return a {Promise} or a {Boolean}. Rejecting the promise or a 52 | * falsey value will schedule the next vote. 53 | * @param [delayInitialTask] {boolean} if truthy, the first vote is scheduled 54 | * instead of immediate 55 | * 56 | * @returns {Promise} 57 | */ 58 | return function poll(task, interval, verifier, delayInitialTask) { 59 | var deferred, canceled, reject; 60 | 61 | canceled = false; 62 | deferred = cancelable(when.defer(), function () { canceled = true; }); 63 | reject = deferred.reject; 64 | 65 | verifier = verifier || function () { return false; }; 66 | 67 | if (typeof interval !== 'function') { 68 | interval = (function (interval) { 69 | return function () { return when().delay(interval); }; 70 | })(interval); 71 | } 72 | 73 | function certify(result) { 74 | deferred.resolve(result); 75 | } 76 | 77 | function schedule(result) { 78 | attempt(interval).then(vote, reject); 79 | if (result !== void 0) { 80 | deferred.notify(result); 81 | } 82 | } 83 | 84 | function vote() { 85 | if (canceled) { return; } 86 | when(task(), 87 | function (result) { 88 | when(verifier(result), 89 | function (verification) { 90 | return verification ? certify(result) : schedule(result); 91 | }, 92 | function () { schedule(result); } 93 | ); 94 | }, 95 | reject 96 | ); 97 | } 98 | 99 | if (delayInitialTask) { 100 | schedule(); 101 | } else { 102 | // if task() is blocking, vote will also block 103 | vote(); 104 | } 105 | 106 | // make the promise cancelable 107 | deferred.promise = Object.create(deferred.promise); 108 | deferred.promise.cancel = deferred.cancel; 109 | 110 | return deferred.promise; 111 | }; 112 | 113 | }); 114 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 115 | -------------------------------------------------------------------------------- /scripts/browserify-tests.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var glob = require('glob'); 4 | var browserify = require('browserify'); 5 | 6 | var POSIX_SEP = path.posix ? path.posix.sep : '/'; 7 | 8 | var ROOT_DIR = path.resolve(__dirname, '..'); 9 | var outputFile = path.resolve(ROOT_DIR, 'test', 'browser', 'tests.js'); 10 | 11 | var entries = glob(path.join(ROOT_DIR, 'test', '**', '*-test.js'), { sync: true }); 12 | if (path.sep !== POSIX_SEP) { 13 | entries = entries.map(function (entry) { 14 | return entry.split(POSIX_SEP).join(path.sep); 15 | }); 16 | } 17 | 18 | browserify({ 19 | entries: entries 20 | }) 21 | .external('buster') 22 | .bundle() 23 | .pipe(fs.createWriteStream(outputFile)); 24 | -------------------------------------------------------------------------------- /scripts/browserify.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var mkdirp = require('mkdirp'); 5 | var browserify = require('browserify'); 6 | var exorcist = require('exorcist'); 7 | 8 | var ROOT_DIR = path.resolve(__dirname, '..'); 9 | 10 | var CONFIGURATIONS = { 11 | 'es6': { 12 | standaloneName: 'Promise', 13 | entries: [ 14 | path.resolve(ROOT_DIR, 'es6-shim', 'Promise.browserify-es6.js') 15 | ], 16 | outputDir: 'es6-shim', 17 | outputFilename: 'Promise.js' 18 | }, 19 | 'when': { 20 | standaloneName: 'when', 21 | entries: [ 22 | path.resolve(ROOT_DIR, 'build', 'when.browserify.js') 23 | ], 24 | outputDir: path.join('dist', 'browser'), 25 | outputFilename: 'when.js' 26 | }, 27 | 'debug': { 28 | standaloneName: 'when', 29 | entries: [ 30 | path.resolve(ROOT_DIR, 'build', 'when.browserify-debug.js') 31 | ], 32 | outputDir: path.join('dist', 'browser'), 33 | outputFilename: 'when.debug.js' 34 | } 35 | }; 36 | 37 | function revParse(callback) { 38 | exec('git rev-parse HEAD', function(err, stdout, stderr) { 39 | process.stderr.write(stderr); 40 | if (err) { 41 | callback(err); 42 | } else { 43 | callback(null, stdout.replace(/(^\s+)|(\s+$)/g, '')); 44 | } 45 | }); 46 | } 47 | 48 | var configName = process.argv[2]; 49 | var config = CONFIGURATIONS[configName]; 50 | 51 | if (!config) { 52 | console.error('Cannot find configuration "' + configName + '"'); 53 | process.exit(1); 54 | return; 55 | } 56 | 57 | mkdirp(config.outputDir, function(mkdirErr) { 58 | if (mkdirErr) { 59 | console.error(mkdirErr); 60 | process.exit(1); 61 | } else { 62 | revParse(function(revParseErr, rev) { 63 | if (revParseErr) { 64 | console.error(revParseErr); 65 | process.exit(1); 66 | } else { 67 | var rootUrl = 'https://raw.githubusercontent.com/cujojs/when/' + rev; 68 | var outputMapFile = path.resolve(ROOT_DIR, config.outputDir, config.outputFilename + '.map'); 69 | var outputFile = path.resolve(ROOT_DIR, config.outputDir, config.outputFilename); 70 | browserify({ 71 | entries: config.entries 72 | }) 73 | .bundle({ 74 | standalone: config.standaloneName, 75 | detectGlobals: false, 76 | debug: true 77 | }) 78 | .pipe(exorcist(outputMapFile, null, rootUrl, ROOT_DIR)) 79 | .pipe(fs.createWriteStream(outputFile)); 80 | } 81 | }); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /sequence.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * sequence.js 5 | * 6 | * Run a set of task functions in sequence. All tasks will 7 | * receive the same args. 8 | * 9 | * @author Brian Cavalier 10 | * @author John Hann 11 | */ 12 | 13 | (function(define) { 14 | define(function(require) { 15 | 16 | var when = require('./when'); 17 | var all = when.Promise.all; 18 | var slice = Array.prototype.slice; 19 | 20 | /** 21 | * Run array of tasks in sequence with no overlap 22 | * @param tasks {Array|Promise} array or promiseForArray of task functions 23 | * @param [args] {*} arguments to be passed to all tasks 24 | * @return {Promise} promise for an array containing 25 | * the result of each task in the array position corresponding 26 | * to position of the task in the tasks array 27 | */ 28 | return function sequence(tasks /*, args... */) { 29 | var results = []; 30 | 31 | return all(slice.call(arguments, 1)).then(function(args) { 32 | return when.reduce(tasks, function(results, task) { 33 | return when(task.apply(void 0, args), addResult); 34 | }, results); 35 | }); 36 | 37 | function addResult(result) { 38 | results.push(result); 39 | return results; 40 | } 41 | }; 42 | 43 | }); 44 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/all-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var CorePromise = when.Promise; 7 | var resolved = when.resolve; 8 | var rejected = when.reject; 9 | 10 | var sentinel = { value: 'sentinel' }; 11 | var other = { value: 'other' }; 12 | 13 | buster.testCase('when.all', { 14 | 15 | 'should resolve empty input': function(done) { 16 | return when.all([]).then( 17 | function(result) { 18 | assert.equals(result, []); 19 | }, 20 | fail 21 | ).ensure(done); 22 | }, 23 | 24 | 'should resolve values array': function(done) { 25 | var input = [1, 2, 3]; 26 | when.all(input).then( 27 | function(results) { 28 | assert.equals(results, input); 29 | }, 30 | fail 31 | ).ensure(done); 32 | }, 33 | 34 | 'should resolve promises array': function(done) { 35 | var input = [resolved(1), resolved(2), resolved(3)]; 36 | when.all(input).then( 37 | function(results) { 38 | assert.equals(results, [1, 2, 3]); 39 | }, 40 | fail 41 | ).ensure(done); 42 | }, 43 | 44 | 'should resolve sparse array input': function(done) { 45 | var input = [, 1, , 1, 1 ]; 46 | when.all(input).then( 47 | function(results) { 48 | assert.equals(results, input); 49 | }, 50 | fail 51 | ).ensure(done); 52 | }, 53 | 54 | 'should reject if any input promise rejects': function(done) { 55 | var input = [resolved(1), rejected(2), resolved(3)]; 56 | when.all(input).then( 57 | fail, 58 | function(failed) { 59 | assert.equals(failed, 2); 60 | } 61 | ).ensure(done); 62 | }, 63 | 64 | 'should accept a promise for an array': function(done) { 65 | var expected, input; 66 | 67 | expected = [1, 2, 3]; 68 | input = resolved(expected); 69 | 70 | when.all(input).then( 71 | function(results) { 72 | assert.equals(results, expected); 73 | }, 74 | fail 75 | ).ensure(done); 76 | }, 77 | 78 | 'should resolve to empty array when input promise does not resolve to array': function(done) { 79 | when.all(resolved(1)).then( 80 | function(result) { 81 | assert.equals(result, []); 82 | }, 83 | fail 84 | ).ensure(done); 85 | }, 86 | 87 | 'should report only 1 unhandled rejection': { 88 | 'when array contains > 1 rejection': function(done) { 89 | /*global setTimeout*/ 90 | var origOnUnhandled = CorePromise.onPotentiallyUnhandledRejection; 91 | CorePromise.onPotentiallyUnhandledRejection = function() { 92 | fail(new Error('should not report unhandled rejection')); 93 | }; 94 | 95 | when.all([rejected(sentinel), resolved(123), rejected(other)]) 96 | ['catch'](function(e) { 97 | assert.same(e, sentinel); 98 | setTimeout(function() { 99 | CorePromise.onPotentiallyUnhandledRejection = origOnUnhandled; 100 | done(); 101 | }, 100); 102 | }); 103 | }, 104 | 105 | 'when array contains same rejection multiple times': function(done) { 106 | /*global setTimeout*/ 107 | var origOnUnhandled = CorePromise.onPotentiallyUnhandledRejection; 108 | CorePromise.onPotentiallyUnhandledRejection = function() { 109 | fail(new Error('should not report unhandled rejection')); 110 | }; 111 | 112 | var r = rejected(sentinel); 113 | when.all([r, r.then()]) 114 | ['catch'](function(e) { 115 | assert.same(e, sentinel); 116 | setTimeout(function() { 117 | CorePromise.onPotentiallyUnhandledRejection = origOnUnhandled; 118 | done(); 119 | }, 100); 120 | }); 121 | } 122 | } 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /test/any-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | var fail = buster.referee.fail; 5 | 6 | var when = require('../when'); 7 | var resolved = when.resolve; 8 | var rejected = when.reject; 9 | 10 | function contains(array, item) { 11 | for(var i=array.length - 1; i >= 0; --i) { 12 | if(array[i] === item) { 13 | return true; 14 | } 15 | } 16 | 17 | return false; 18 | } 19 | 20 | buster.testCase('when.any', { 21 | 22 | 'should reject with RangeError': { 23 | 'when zero inputs': function() { 24 | return when.any([])['catch']( 25 | function (e) { 26 | assert(e instanceof RangeError); 27 | }); 28 | }, 29 | 30 | 'when input promise does not resolve to array': function() { 31 | return when.any(when.resolve(1))['catch']( 32 | function(e) { 33 | assert(e instanceof RangeError); 34 | }); 35 | } 36 | }, 37 | 38 | 'should reject with all rejected input values if all inputs are rejected': function() { 39 | var input = [rejected(1), rejected(2), rejected(3)]; 40 | return when.any(input)['catch']( 41 | function(result) { 42 | assert.equals(result, [1, 2, 3]); 43 | } 44 | ); 45 | }, 46 | 47 | 'should resolve with an input value': function() { 48 | var input = [1, 2, 3]; 49 | return when.any(input).then( 50 | function(result) { 51 | assert(contains(input, result)); 52 | }, 53 | fail 54 | ); 55 | }, 56 | 57 | 'should resolve with a promised input value': function() { 58 | var input = [resolved(1), resolved(2), resolved(3)]; 59 | return when.any(input).then( 60 | function(result) { 61 | assert(contains([1, 2, 3], result)); 62 | } 63 | ); 64 | }, 65 | 66 | 'should accept a promise for an array': function() { 67 | var expected, input; 68 | 69 | expected = [1, 2, 3]; 70 | input = resolved(expected); 71 | 72 | return when.any(input).then( 73 | function(result) { 74 | refute.equals(expected.indexOf(result), -1); 75 | } 76 | ); 77 | } 78 | 79 | }); 80 | -------------------------------------------------------------------------------- /test/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | when.js browser tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/browsers.json: -------------------------------------------------------------------------------- 1 | [ 2 | // we don't really care about the platform, but without it the browser may fail to resolve 3 | { browserName: 'chrome', platform: 'Windows 8.1' }, 4 | { browserName: 'firefox', platform: 'Windows 8.1' }, 5 | { browserName: 'internet explorer', version: '11', platform: 'Windows 8.1' }, 6 | { browserName: 'internet explorer', version: '10', platform: 'Windows 8' }, 7 | { browserName: 'internet explorer', version: '9', platform: 'Windows 7' }, 8 | { browserName: 'opera', version: '12', platform: 'Windows 7' }, 9 | { browserName: 'opera', version: '11', platform: 'Windows 7' }, 10 | { browserName: 'safari', version: '7', platform: 'OS X 10.9' }, 11 | { browserName: 'safari', version: '6', platform: 'OS X 10.8' }, 12 | { browserName: 'ipad', version: '7.1', platform: 'OS X 10.9' }, 13 | { browserName: 'ipad', version: '7', platform: 'OS X 10.9' } 14 | ] -------------------------------------------------------------------------------- /test/buster.js: -------------------------------------------------------------------------------- 1 | exports.node = { 2 | environment: 'node', 3 | rootPath: '../', 4 | tests: [ 5 | 'test/**/*-test.js' 6 | ] 7 | }; 8 | 9 | exports.browser = { 10 | environment: 'browser', 11 | rootPath: '..', 12 | tests: [ 13 | 'test/browser/tests.js' 14 | ], 15 | resources: [ 16 | 'test/browser/es5.js' 17 | ], 18 | testbed: 'test/browser/index.html' 19 | }; 20 | -------------------------------------------------------------------------------- /test/cancelable-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var cancelable = require('../cancelable'); 7 | 8 | var sentinel = {}; 9 | var other = {}; 10 | 11 | buster.testCase('when/cancelable', { 12 | 'should decorate deferred with a cancel() method': function() { 13 | var c = cancelable(when.defer(), function() {}); 14 | assert(typeof c.cancel == 'function'); 15 | }, 16 | 17 | 'should propagate a rejection when a cancelable deferred is canceled': function(done) { 18 | var c = cancelable(when.defer(), function() { return sentinel; }); 19 | c.cancel(); 20 | 21 | c.promise.then( 22 | fail, 23 | function(v) { 24 | assert.equals(v, sentinel); 25 | } 26 | ).ensure(done); 27 | }, 28 | 29 | 'should return a promise for canceled value when canceled': function(done) { 30 | var c, promise; 31 | 32 | c = cancelable(when.defer(), function() { return sentinel; }); 33 | promise = c.cancel(); 34 | 35 | promise.then( 36 | fail, 37 | function(v) { 38 | assert.equals(v, sentinel); 39 | } 40 | ).ensure(done); 41 | }, 42 | 43 | 'should not invoke canceler when rejected normally': function(done) { 44 | var c = cancelable(when.defer(), function() { return other; }); 45 | c.reject(sentinel); 46 | c.cancel(); 47 | 48 | c.promise.then( 49 | fail, 50 | function(v) { 51 | assert.equals(v, sentinel); 52 | } 53 | ).ensure(done); 54 | }, 55 | 56 | 'should propagate the unaltered resolution value': function(done) { 57 | var c = cancelable(when.defer(), function() { return other; }); 58 | c.resolve(sentinel); 59 | c.cancel(); 60 | 61 | c.promise.then( 62 | function(val) { 63 | assert.same(val, sentinel); 64 | }, 65 | function(e) { 66 | fail(e); 67 | } 68 | ).ensure(done); 69 | }, 70 | 71 | 'should call progback for cancelable deferred': function(done) { 72 | var c = cancelable(when.defer()); 73 | 74 | c.promise.then(null, null, function (status) { 75 | assert.same(status, sentinel); 76 | done(); 77 | }); 78 | 79 | c.notify(sentinel); 80 | } 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /test/cycle-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var CorePromise = require('../lib/Promise'); 5 | 6 | function assertCycle(p) { 7 | return p.then(buster.referee.fail, function(e) { 8 | assert(e instanceof TypeError); 9 | }); 10 | } 11 | 12 | buster.testCase('cycle detection', { 13 | 14 | 'should detect self-cycles': { 15 | 'when resolving': function() { 16 | /*global setTimeout*/ 17 | var p = new CorePromise(function(resolve) { 18 | setTimeout(function() { 19 | resolve(p); 20 | }, 0); 21 | }); 22 | 23 | return assertCycle(p); 24 | }, 25 | 26 | 'when returning from handler': function() { 27 | var p = CorePromise.resolve(); 28 | p = p.then(function() { 29 | return p; 30 | }); 31 | 32 | return assertCycle(p); 33 | }, 34 | 35 | 'when returning resolved from handler': function() { 36 | var p = CorePromise.resolve(); 37 | p = p.then(function() { 38 | return CorePromise.resolve(p); 39 | }); 40 | 41 | return assertCycle(p); 42 | } 43 | }, 44 | 45 | 'should detect long cycles': function() { 46 | var p1 = new CorePromise(function(resolve) { 47 | setTimeout(function() { 48 | resolve(p2); 49 | }, 0); 50 | }); 51 | 52 | var p2 = new CorePromise(function(resolve) { 53 | setTimeout(function() { 54 | resolve(p3); 55 | }, 0); 56 | }); 57 | 58 | var p3 = new CorePromise(function(resolve) { 59 | setTimeout(function() { 60 | resolve(p1); 61 | }, 0); 62 | }); 63 | 64 | return assertCycle(p3); 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /test/delay-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var delay = require('../delay'); 7 | 8 | var sentinel = {}; 9 | 10 | function now() { 11 | return (new Date()).getTime(); 12 | } 13 | 14 | 15 | buster.testCase('when/delay', { 16 | 'should resolve after delay': function(done) { 17 | delay(0).then( 18 | function() { 19 | assert(true); 20 | }, 21 | fail 22 | ).ensure(done); 23 | }, 24 | 25 | 'should resolve with provided value after delay': function(done) { 26 | delay(0, sentinel).then( 27 | function(val) { 28 | assert.same(val, sentinel); 29 | done(); 30 | }, 31 | fail 32 | ).ensure(done); 33 | }, 34 | 35 | 'should delay by the provided value': function(done) { 36 | var start = now(); 37 | 38 | delay(100).then( 39 | function() { 40 | assert((now() - start) > 50); 41 | }, 42 | fail 43 | ).ensure(done); 44 | }, 45 | 46 | 'should resolve after input promise plus delay': function(done) { 47 | when.resolve(sentinel).delay(10).then( 48 | function(val) { 49 | assert.equals(val, sentinel); 50 | }, 51 | fail 52 | ).ensure(done); 53 | }, 54 | 55 | 'should not delay if rejected': function(done) { 56 | var d = when.defer(); 57 | d.reject(sentinel); 58 | 59 | d.promise.delay(0).then( 60 | fail, 61 | function(val) { 62 | assert.equals(val, sentinel); 63 | } 64 | ).ensure(done); 65 | }, 66 | 67 | 'should propagate progress': function(done) { 68 | var d = when.defer(); 69 | 70 | d.promise.delay(0).then(null, null, 71 | function(val) { 72 | assert.same(val, sentinel); 73 | d.resolve(); 74 | } 75 | ).ensure(done); 76 | 77 | d.notify(sentinel); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /test/else-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var when = require('../when'); 5 | 6 | var input = {}; 7 | var sentinel = { value: 'sentinel' }; 8 | 9 | buster.testCase('promise.else', { 10 | 'should resolve normally if previous promise doesn\'t fail': function () { 11 | 12 | return when.resolve(input) 13 | ['else'](sentinel) 14 | .then(function (val) { 15 | assert.same(val, input); 16 | }); 17 | }, 18 | 19 | 'should resolve with else value if previous promise fails': function () { 20 | 21 | return when.reject(input) 22 | ['else'](sentinel) 23 | .then(function (val) { 24 | assert.same(val, sentinel); 25 | }); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /test/filter-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var when = require('../when'); 5 | var sentinel = { value: 'sentinel' }; 6 | 7 | function even(x) { 8 | return x % 2 === 0; 9 | } 10 | 11 | function evenPromise(x) { 12 | return when(even(x)); 13 | } 14 | 15 | buster.testCase('when.filter', { 16 | 17 | 'should pass index to predicate as second param': function() { 18 | return when.filter(['a','b','c'], function(x, i) { 19 | assert(typeof i === 'number'); 20 | return true; 21 | }); 22 | }, 23 | 24 | 'should filter input values array': function() { 25 | var input = [1, 2, 3]; 26 | return when.filter(input, even).then( 27 | function(results) { 28 | assert.equals(input.filter(even), results); 29 | }); 30 | }, 31 | 32 | 'should filter input promises array': function() { 33 | var input = [1, 2, 3]; 34 | return when.filter(input.map(when), even).then( 35 | function(results) { 36 | assert.equals(input.filter(even), results); 37 | }); 38 | }, 39 | 40 | 'should filter input when predicate returns a promise': function() { 41 | var input = [1,2,3]; 42 | return when.filter(input, evenPromise).then( 43 | function(results) { 44 | assert.equals(input.filter(even), results); 45 | }); 46 | }, 47 | 48 | 'should accept a promise for an array': function() { 49 | var input = [1,2,3]; 50 | return when.filter(when(input), even).then( 51 | function(results) { 52 | assert.equals(input.filter(even), results); 53 | }); 54 | }, 55 | 56 | 'should fulfill with empty array when input promise fulfills with non-array': function() { 57 | return when.filter(when(123), even).then( 58 | function(result) { 59 | assert.equals(result, []); 60 | }); 61 | }, 62 | 63 | 'should reject when input contains rejection': function() { 64 | var input = [when(1), when.reject(sentinel), 3]; 65 | return when.filter(input, even)['catch']( 66 | function(e) { 67 | assert.same(e, sentinel); 68 | }); 69 | }, 70 | 71 | 'should reject when input is a rejected promise': function() { 72 | return when.filter(when.reject(sentinel), even)['catch']( 73 | function(e) { 74 | assert.same(e, sentinel); 75 | }); 76 | }, 77 | 78 | 'should match Array.prototype.filter behavior when predicate modifies array': function() { 79 | // Test to match Array.prototype.filter behavior 80 | var a = [1, 2, 3, 4]; 81 | var b = a.slice(); 82 | var expected = b.filter(makePredicate(b)); 83 | 84 | function makePredicate(a) { 85 | return function (n, i){ 86 | a[i] = 'fail'; 87 | return n % 2 === 0; 88 | }; 89 | } 90 | 91 | return when.filter(a, makePredicate(a)).then(function(results) { 92 | assert.equals(results, expected); 93 | }); 94 | } 95 | 96 | }); 97 | -------------------------------------------------------------------------------- /test/flow-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var CorePromise = require('../when').Promise; 6 | 7 | var sentinel = { value: 'sentinel' }; 8 | var other = { value: 'other' }; 9 | 10 | var origOnUnhandled = CorePromise.onPotentiallyUnhandledRejection; 11 | var origOnHandled = CorePromise.onPotentiallyUnhandledRejectionHandled; 12 | 13 | buster.testCase('when/lib/flow', { 14 | 15 | 'otherwise': { 16 | 'should be an alias for catch': function() { 17 | assert.same(CorePromise.prototype['catch'], CorePromise.prototype.otherwise); 18 | } 19 | }, 20 | 21 | 'catch': { 22 | 'should catch rejections': function() { 23 | return CorePromise.reject(sentinel)['catch'](function(e) { 24 | assert.same(e, sentinel); 25 | }); 26 | }, 27 | 28 | 'when predicate is provided': { 29 | 30 | 'and is an Error type match': { 31 | 'should only catch errors of same type': function() { 32 | var e1 = new TypeError(); 33 | return CorePromise.reject(e1)['catch'](SyntaxError, fail) 34 | ['catch'](TypeError, function(e) { 35 | assert.same(e1, e); 36 | }); 37 | } 38 | }, 39 | 40 | 'and is a predicate function': { 41 | 'should only catch errors of same type': function() { 42 | var e1 = new TypeError(); 43 | return CorePromise.reject(e1)['catch'](function(e) { 44 | return e !== e1; 45 | }, fail)['catch'](function(e) { 46 | return e === e1; 47 | }, function(e) { 48 | assert.same(e1, e); 49 | }); 50 | } 51 | }, 52 | 53 | 'but is not a function': { 54 | 'when rejected should reject with a TypeError': function() { 55 | return CorePromise.reject(sentinel)['catch'](123, fail) 56 | ['catch'](function(e) { 57 | assert(e instanceof TypeError); 58 | }); 59 | }, 60 | 61 | 'when fulfilled should reject with a TypeError': function() { 62 | return CorePromise.resolve(sentinel)['catch'](123, fail) 63 | ['catch'](function(e) { 64 | assert(e instanceof TypeError); 65 | }); 66 | } 67 | 68 | } 69 | } 70 | }, 71 | 72 | 'finally': { 73 | 'should be an alias for ensure': function() { 74 | var p = CorePromise.resolve(); 75 | assert.same(p['finally'], p.ensure); 76 | } 77 | }, 78 | 79 | 'ensure': { 80 | 'should return a promise': function() { 81 | assert.isFunction(CorePromise.resolve().ensure().then); 82 | }, 83 | 84 | 'should not suppress unhandled rejection': { 85 | tearDown: function() { 86 | CorePromise.onPotentiallyUnhandledRejection = origOnUnhandled; 87 | CorePromise.onPotentiallyUnhandledRejectionHandled = origOnHandled; 88 | }, 89 | 90 | 'when handler returns non-promise': function(done) { 91 | CorePromise.onPotentiallyUnhandledRejection = function() { 92 | assert(true); 93 | done(); 94 | }; 95 | 96 | CorePromise.reject(sentinel).ensure(function() {}); 97 | }, 98 | 99 | 'when handler returns promise': function(done) { 100 | CorePromise.onPotentiallyUnhandledRejection = function() { 101 | assert(true); 102 | done(); 103 | }; 104 | 105 | CorePromise.reject(sentinel).ensure(function() { 106 | return CorePromise.resolve(other); 107 | }); 108 | }, 109 | 110 | 'when finally handler throws': function(done) { 111 | /*global setTimeout*/ 112 | var errors = {}; 113 | CorePromise.onPotentiallyUnhandledRejection = function(rej) { 114 | errors[rej.errorId] = rej.value; 115 | }; 116 | 117 | CorePromise.onPotentiallyUnhandledRejectionHandled = function(rej) { 118 | delete errors[rej.errorId]; 119 | }; 120 | 121 | CorePromise.reject(other).ensure(function() { 122 | throw sentinel; 123 | }); 124 | 125 | setTimeout(done(function() { 126 | var keys = Object.keys(errors); 127 | assert.equals(keys.length, 1); 128 | assert.same(errors[keys[0]], sentinel); 129 | }), 100); 130 | }, 131 | 132 | 'when finally handler rejects': function(done) { 133 | /*global setTimeout*/ 134 | var errors = {}; 135 | CorePromise.onPotentiallyUnhandledRejection = function(rej) { 136 | errors[rej.errorId] = rej.value; 137 | }; 138 | 139 | CorePromise.onPotentiallyUnhandledRejectionHandled = function(rej) { 140 | delete errors[rej.errorId]; 141 | }; 142 | 143 | CorePromise.reject(other).ensure(function() { 144 | return CorePromise.reject(sentinel); 145 | }); 146 | 147 | setTimeout(done(function() { 148 | var keys = Object.keys(errors); 149 | assert.equals(keys.length, 1); 150 | assert.same(errors[keys[0]], sentinel); 151 | }), 100); 152 | } 153 | }, 154 | 155 | 'when fulfilled': { 156 | 'should ignore callback return value': function() { 157 | return CorePromise.resolve(sentinel).ensure( 158 | function() { 159 | return other; 160 | } 161 | ).then( 162 | function(val) { 163 | assert.same(val, sentinel); 164 | }, 165 | fail 166 | ); 167 | }, 168 | 169 | 'should await returned promise': function() { 170 | var awaited = false; 171 | return CorePromise.resolve(sentinel).ensure(function() { 172 | return new CorePromise(function(resolve) { 173 | setTimeout(function() { 174 | awaited = true; 175 | resolve(); 176 | }, 1); 177 | }); 178 | }).then(function() { 179 | assert(awaited); 180 | }); 181 | }, 182 | 183 | 'should propagate rejection on throw': function() { 184 | return CorePromise.resolve(other).ensure( 185 | function() { 186 | throw sentinel; 187 | } 188 | ).then( 189 | fail, 190 | function(val) { 191 | assert.same(val, sentinel); 192 | } 193 | ); 194 | } 195 | }, 196 | 197 | 'when rejected': { 198 | 'should propagate rejection, ignoring callback return value': function() { 199 | return CorePromise.reject(sentinel).ensure( 200 | function() { 201 | return other; 202 | } 203 | ).then( 204 | fail, 205 | function(val) { 206 | assert.same(val, sentinel); 207 | } 208 | ); 209 | }, 210 | 211 | 'should await returned promise': function() { 212 | var awaited = false; 213 | return CorePromise.resolve(sentinel).ensure(function() { 214 | return new CorePromise(function(resolve, reject) { 215 | setTimeout(function() { 216 | awaited = true; 217 | reject(); 218 | }, 1); 219 | }); 220 | })['catch'](function() { 221 | assert(awaited); 222 | }); 223 | }, 224 | 225 | 'should propagate rejection on throw': function() { 226 | return CorePromise.reject(other).ensure( 227 | function() { 228 | throw sentinel; 229 | } 230 | ).then( 231 | fail, 232 | function(val) { 233 | assert.same(val, sentinel); 234 | } 235 | ); 236 | } 237 | }, 238 | 239 | 'should ignore non-function': function() { 240 | return CorePromise.resolve(true).ensure().then(assert); 241 | } 242 | } 243 | }); 244 | -------------------------------------------------------------------------------- /test/fold-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var when = require('../when'); 4 | var sentinel = { value: 'sentinel' }; 5 | var other = { value: 'other' }; 6 | 7 | function noop() {} 8 | 9 | buster.testCase('promise.fold', { 10 | 11 | 'should pass value and arg': function() { 12 | return when.resolve(other).fold(function(a, b) { 13 | assert.same(a, sentinel); 14 | assert.same(b, other); 15 | }, sentinel); 16 | }, 17 | 18 | 'should pairwise combine two promises': function() { 19 | return when.resolve(1).fold(function sum(x, y) { 20 | return x + y; 21 | }, when.resolve(2)).then(function(x){ 22 | assert.equals(x, 3); 23 | }); 24 | }, 25 | 26 | 'should reject if combine throws': function() { 27 | return when.resolve(1).fold(function() { 28 | throw sentinel; 29 | }, 2)['catch'](function(e){ 30 | assert.same(e, sentinel); 31 | }); 32 | }, 33 | 34 | 'should reject if combine returns rejection': function() { 35 | return when.resolve(1).fold(when.reject, sentinel)['catch'](function(e){ 36 | assert.same(e, sentinel); 37 | }); 38 | }, 39 | 40 | 'should reject and not call combine': { 41 | 'if promise rejects': function() { 42 | return when.reject(sentinel).fold(noop, 2)['catch'](function(e){ 43 | assert.same(e, sentinel); 44 | }); 45 | }, 46 | 47 | 'if arg rejects': function() { 48 | return when.resolve(1).fold(noop, when.reject(sentinel)) 49 | ['catch'](function(e){ 50 | assert.same(e, sentinel); 51 | }); 52 | } 53 | } 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/format-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var format = require('../lib/format'); 5 | 6 | buster.testCase('format', { 7 | 8 | 'formatError': { 9 | 'should format null as string': function() { 10 | var s = format.formatError(null); 11 | assert.equals(typeof s, 'string'); 12 | }, 13 | 14 | 'should format undefined as string': function() { 15 | var s = format.formatError(void 0); 16 | assert.equals(typeof s, 'string'); 17 | }, 18 | 19 | 'should be the contents of the stack property of an error': function() { 20 | var expected = 'ok'; 21 | var e = new Error(); 22 | e.stack = expected; 23 | var s = format.formatError(e); 24 | assert.equals(s, expected); 25 | } 26 | 27 | }, 28 | 29 | 'formatObject': { 30 | 'should JSON.stringify a plain object': function() { 31 | var o = {foo: 'bar'}; 32 | var s = format.formatObject(o); 33 | assert.equals(s, JSON.stringify(o)); 34 | } 35 | }, 36 | 37 | 'tryStringify': { 38 | 'should return default value when JSON.stringify fails': function() { 39 | var o = { circle: null }; 40 | o.circle = o; 41 | 42 | var sentinel = {}; 43 | assert.same(sentinel, format.tryStringify(o, sentinel)); 44 | } 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /test/globalRejectionEvents-test.js: -------------------------------------------------------------------------------- 1 | /*global process, window, setTimeout*/ 2 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 3 | 4 | var CorePromise = require('../lib/Promise'); 5 | 6 | var sentinel = { value: 'sentinel' }; 7 | 8 | buster.testCase('global rejection events', { 9 | 10 | 'on Node': { 11 | 'tearDown': function() { 12 | if(typeof window !== 'undefined') { 13 | return; 14 | } 15 | 16 | process.removeAllListeners('unhandledRejection'); 17 | process.removeAllListeners('rejectionHandled'); 18 | }, 19 | 20 | 'should emit unhandledRejection': function(done) { 21 | if(typeof window !== 'undefined') { 22 | buster.assert(true); 23 | done(); 24 | return; 25 | } 26 | 27 | function listener(e) { 28 | buster.assert.same(e, sentinel); 29 | done(); 30 | } 31 | 32 | process.on('unhandledRejection', listener); 33 | 34 | CorePromise.reject(sentinel); 35 | }, 36 | 37 | 'should emit rejectionHandled': function(done) { 38 | if(typeof window !== 'undefined') { 39 | buster.assert(true); 40 | done(); 41 | return; 42 | } 43 | 44 | var r; 45 | function unhandled(e, rejection) { 46 | buster.assert.same(e, sentinel); 47 | r = rejection; 48 | } 49 | 50 | function handled(rejection) { 51 | buster.assert.same(rejection, r); 52 | done(); 53 | } 54 | 55 | process.on('unhandledRejection', unhandled); 56 | process.on('rejectionHandled', handled); 57 | 58 | var p = CorePromise.reject(sentinel); 59 | setTimeout(function() { 60 | p.catch(function() {}); 61 | }, 10); 62 | } 63 | }, 64 | 65 | 'in Browser': { 66 | 'should emit unhandledRejection': function(done) { 67 | if(typeof window === 'undefined') { 68 | buster.assert(true); 69 | done(); 70 | return; 71 | } 72 | 73 | function listener(e) { 74 | window.removeEventListener('unhandledRejection', listener, false); 75 | e.preventDefault(); 76 | buster.assert.same(e.detail.reason, sentinel); 77 | done(); 78 | } 79 | 80 | window.addEventListener('unhandledRejection', listener, false); 81 | 82 | CorePromise.reject(sentinel); 83 | }, 84 | 85 | 'should emit rejectionHandled': function(done) { 86 | if(typeof window === 'undefined') { 87 | buster.assert(true); 88 | done(); 89 | return; 90 | } 91 | 92 | var key; 93 | function unhandled(e) { 94 | window.removeEventListener('unhandledRejection', unhandled, false); 95 | buster.assert.same(e.detail.reason, sentinel); 96 | key = e.detail.key; 97 | } 98 | 99 | function handled(e) { 100 | window.removeEventListener('rejectionHandled', handled, false); 101 | buster.assert.same(e.detail.key, key); 102 | done(); 103 | } 104 | 105 | window.addEventListener('unhandledRejection', unhandled, false); 106 | window.addEventListener('rejectionHandled', handled, false); 107 | 108 | var p = CorePromise.reject(sentinel); 109 | setTimeout(function() { 110 | p.catch(function() {}); 111 | }, 10); 112 | } 113 | } 114 | 115 | }); 116 | 117 | -------------------------------------------------------------------------------- /test/guard-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var guard = require('../guard'); 7 | 8 | var sentinel = {}; 9 | var other = {}; 10 | 11 | function noop() {} 12 | 13 | buster.testCase('when/guard', { 14 | 15 | 'should return a function': function() { 16 | assert.isFunction(guard()); 17 | }, 18 | 19 | 'should invoke condition': function() { 20 | var condition, guarded; 21 | 22 | condition = this.spy(); 23 | guarded = guard(condition, noop); 24 | 25 | guarded(); 26 | 27 | assert.called(condition); 28 | }, 29 | 30 | 'should invoke guarded function after condition promise fulfills': function(done) { 31 | var condition, f, guarded; 32 | 33 | condition = function() { return noop; }; 34 | f = this.spy(); 35 | guarded = guard(condition, f); 36 | 37 | guarded(sentinel).then( 38 | function() { 39 | assert.calledOnce(f); 40 | assert.same(f.firstCall.args[0], sentinel); 41 | }, 42 | fail 43 | ).ensure(done); 44 | }, 45 | 46 | 'should notify condition once guarded function settles': function(done) { 47 | var condition, notify, guarded; 48 | 49 | notify = this.spy(); 50 | condition = function() { return notify; }; 51 | guarded = guard(condition, noop); 52 | 53 | guarded().then( 54 | function() { 55 | assert.calledOnce(notify); 56 | }, 57 | fail 58 | ).ensure(done); 59 | }, 60 | 61 | 'should initiate next guarded call after notify': function(done) { 62 | var condition, f, guarded; 63 | 64 | f = this.spy(); 65 | condition = function() { return noop; }; 66 | guarded = guard(condition, f); 67 | 68 | guarded(other).then( 69 | function() { 70 | assert.calledOnce(f); 71 | return guarded(sentinel).then(function() { 72 | assert.calledTwice(f); 73 | assert.same(f.secondCall.args[0], sentinel); 74 | }); 75 | }, 76 | fail 77 | ).ensure(done); 78 | }, 79 | 80 | 'n': { 81 | 'should create a function': function() { 82 | assert.isFunction(guard.n(1)); 83 | }, 84 | 85 | 'should return a promise': function() { 86 | var c = guard.n(1); 87 | assert.isFunction(c().then); 88 | }, 89 | 90 | 'returned promise should resolve to a function': function(done) { 91 | var enter = guard.n(1); 92 | enter().then( 93 | function(exit) { 94 | assert.isFunction(exit); 95 | }, 96 | fail 97 | ).ensure(done); 98 | }, 99 | 100 | 'should allow one execution': function(done) { 101 | var enter, value, first, second; 102 | 103 | enter = guard.n(1); 104 | value = sentinel; 105 | 106 | first = enter(); 107 | second = enter(); 108 | 109 | first.then( 110 | function(exit) { 111 | return when().delay(100).then(function() { 112 | assert.same(value, sentinel); 113 | exit(); 114 | }); 115 | }, 116 | fail 117 | ); 118 | 119 | second.then( 120 | function() { value = other; } 121 | ).ensure(done); 122 | }, 123 | 124 | 'should allow two executions': function(done) { 125 | var one, value, first, second, third; 126 | 127 | one = guard.n(2); 128 | value = sentinel; 129 | 130 | first = one(); 131 | second = one(); 132 | third = one(); 133 | 134 | first.then( 135 | function() { 136 | assert.same(value, sentinel); 137 | }, 138 | fail 139 | ); 140 | 141 | second.then( 142 | function(exit) { 143 | return when().delay(100).then(function() { 144 | assert.same(value, sentinel); 145 | exit(); 146 | }); 147 | }, 148 | fail 149 | ); 150 | 151 | third.then( 152 | function() { value = other; } 153 | ).ensure(done); 154 | } 155 | } 156 | 157 | }); 158 | -------------------------------------------------------------------------------- /test/inspect-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var inspect = require('../lib/decorators/inspect'); 6 | var CorePromise = inspect(require('../lib/Promise')); 7 | 8 | var sentinel = { value: 'sentinel' }; 9 | 10 | function assertPending(s) { 11 | assert.equals(s.state, 'pending'); 12 | } 13 | 14 | function assertFulfilled(s, value) { 15 | assert.equals(s.state, 'fulfilled'); 16 | assert.same(s.value, value); 17 | } 18 | 19 | function assertRejected(s, reason) { 20 | assert.equals(s.state, 'rejected'); 21 | assert.same(s.reason, reason); 22 | } 23 | 24 | buster.testCase('inspect', { 25 | 26 | 'when inspecting promises': { 27 | 'should return pending state for pending promise': function() { 28 | var promise = new CorePromise(function() {}); 29 | 30 | assertPending(promise.inspect()); 31 | }, 32 | 33 | 'should immediately return fulfilled state for fulfilled promise': function() { 34 | assertFulfilled(CorePromise.resolve(sentinel).inspect(), sentinel); 35 | }, 36 | 37 | 'should return fulfilled state for fulfilled promise': function() { 38 | var promise = CorePromise.resolve(sentinel); 39 | 40 | return promise.then(function() { 41 | assertFulfilled(promise.inspect(), sentinel); 42 | }); 43 | }, 44 | 45 | 'should immediately return rejected state for rejected promise': function() { 46 | assertRejected(CorePromise.reject(sentinel).inspect(), sentinel); 47 | }, 48 | 49 | 'should return rejected state for rejected promise': function() { 50 | var promise = CorePromise.reject(sentinel); 51 | 52 | return promise.then(fail, function() { 53 | assertRejected(promise.inspect(), sentinel); 54 | }); 55 | } 56 | }, 57 | 58 | 'when inspecting thenables': { 59 | 'should return pending state for pending thenable': function() { 60 | var p = CorePromise.resolve({ then: function() {} }); 61 | 62 | assertPending(p.inspect()); 63 | }, 64 | 65 | 'should return fulfilled state for fulfilled thenable': function() { 66 | var p = CorePromise.resolve({ then: function(fulfill) { fulfill(sentinel); } }); 67 | 68 | return p.then(function() { 69 | assertFulfilled(p.inspect(), sentinel); 70 | }); 71 | }, 72 | 73 | 'should return rejected state for rejected thenable': function() { 74 | var p = CorePromise.resolve({ then: function(_, rejected) { rejected(sentinel); } }); 75 | 76 | return p.then(fail, function() { 77 | assertRejected(p.inspect(), sentinel); 78 | }); 79 | } 80 | } 81 | }); 82 | 83 | -------------------------------------------------------------------------------- /test/isPromiseLike-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | 3 | var when = require('../when'); 4 | 5 | var fakePromise = { 6 | then:function () {} 7 | }; 8 | 9 | function assertIsPromiseLike(it) { 10 | buster.assert(when.isPromiseLike(it)); 11 | } 12 | 13 | function refuteIsPromiseLike(it) { 14 | buster.refute(when.isPromiseLike(it)); 15 | } 16 | 17 | buster.testCase('when.isPromiseLike', { 18 | 19 | 'should return true for trusted': function() { 20 | assertIsPromiseLike(when.resolve()); 21 | }, 22 | 23 | 'should return true for promise': function() { 24 | assertIsPromiseLike(fakePromise); 25 | }, 26 | 27 | 'should return false for non-promise': function() { 28 | /*jshint -W009, -W010, -W053 */ 29 | var inputs = [ 30 | 1, 31 | 0, 32 | 'not a promise', 33 | true, 34 | false, 35 | void 0, 36 | null, 37 | '', 38 | /foo/, 39 | {}, 40 | new Object(), 41 | new RegExp('foo'), 42 | new Date(), 43 | new Boolean(), 44 | [], 45 | new Array() 46 | ]; 47 | 48 | for(var i = inputs.length - 1; i >= 0; --i) { 49 | refuteIsPromiseLike(inputs[i]); 50 | } 51 | }, 52 | 53 | 'should return true for delegated promise': function() { 54 | function T() {} 55 | 56 | T.prototype = fakePromise; 57 | assertIsPromiseLike(new T()); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /test/iterate-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | var fail = buster.referee.fail; 5 | 6 | var when = require('../when'); 7 | 8 | var sentinel = {}; 9 | var other = {}; 10 | 11 | function noop() {} 12 | 13 | 14 | buster.testCase('lib/iterate', { 15 | 16 | 'unfold': { 17 | 'should invoke condition first': function(done) { 18 | function condition() { 19 | return true; 20 | } 21 | 22 | when.unfold(noop, condition, noop, sentinel).then( 23 | function(value) { 24 | assert.same(value, sentinel); 25 | } 26 | ).ensure(done); 27 | }, 28 | 29 | 'should call generator until condition returns truthy': function(done) { 30 | function condition(i) { 31 | return i === 0; 32 | } 33 | 34 | var unspool = this.spy(function(x) { 35 | return [x, x-1]; 36 | }); 37 | 38 | when.unfold(unspool, condition, noop, 3).then( 39 | function() { 40 | assert.equals(unspool.callCount, 3); 41 | } 42 | ).ensure(done); 43 | }, 44 | 45 | 'generator': { 46 | 'should be allowed to return an array of promises': function(done) { 47 | function condition(i) { 48 | return i === 0; 49 | } 50 | 51 | var unspool = this.spy(function(x) { 52 | return [when.resolve(x), when.resolve(x-1)]; 53 | }); 54 | 55 | when.unfold(unspool, condition, noop, 3).then( 56 | function() { 57 | assert.equals(unspool.callCount, 3); 58 | } 59 | ).ensure(done); 60 | }, 61 | 62 | 'should be allowed to return a promise for an array': function(done) { 63 | function condition(i) { 64 | return i === 0; 65 | } 66 | 67 | var unspool = this.spy(function(x) { 68 | return when.resolve([x, x-1]); 69 | }); 70 | 71 | when.unfold(unspool, condition, noop, 3).then( 72 | function() { 73 | assert.equals(unspool.callCount, 3); 74 | } 75 | ).ensure(done); 76 | }, 77 | 78 | 'should be allowed to return a promise for an array of promises': function(done) { 79 | function condition(i) { 80 | return i === 0; 81 | } 82 | 83 | var unspool = this.spy(function(x) { 84 | return when.resolve([when.resolve(x), when.resolve(x-1)]); 85 | }); 86 | 87 | when.unfold(unspool, condition, noop, 3).then( 88 | function() { 89 | assert.equals(unspool.callCount, 3); 90 | } 91 | ).ensure(done); 92 | } 93 | }, 94 | 95 | 'condition': { 96 | 'should be allowed to return a promise that fulfills': function(done) { 97 | function condition(i) { 98 | return when.resolve(i === 0); 99 | } 100 | 101 | var unspool = this.spy(function(x) { 102 | return [x, x-1]; 103 | }); 104 | 105 | when.unfold(unspool, condition, noop, 3).then( 106 | function() { 107 | assert.equals(unspool.callCount, 3); 108 | } 109 | ).ensure(done); 110 | }, 111 | 112 | 'should abort unfold by returning a rejection': function(done) { 113 | function condition() { 114 | return when.reject(); 115 | } 116 | 117 | var unspool = this.spy(); 118 | 119 | when.unfold(unspool, condition, noop, 3).then( 120 | fail, 121 | function() { 122 | refute.called(unspool); 123 | } 124 | ).ensure(done); 125 | } 126 | }, 127 | 128 | 'should call handler with generator result': function(done) { 129 | function condition(i) { 130 | return i === 0; 131 | } 132 | 133 | var handler = this.spy(); 134 | 135 | function generator() { 136 | return [sentinel, 0]; 137 | } 138 | 139 | when.unfold(generator, condition, handler).then( 140 | function() { 141 | assert.calledOnceWith(handler, sentinel); 142 | } 143 | ).ensure(done); 144 | }, 145 | 146 | 'should reject when condition throws': function(done) { 147 | var generator = this.spy(); 148 | var handler = this.spy(); 149 | 150 | function condition() { 151 | throw sentinel; 152 | } 153 | 154 | when.unfold(generator, condition, handler, other).then( 155 | fail, 156 | function(e) { 157 | refute.called(generator); 158 | refute.called(handler); 159 | assert.same(e, sentinel); 160 | } 161 | ).ensure(done); 162 | 163 | }, 164 | 165 | 'should reject when generator throws': function(done) { 166 | var handler = this.spy(); 167 | 168 | function condition() { 169 | return false; 170 | } 171 | function generator() { 172 | throw sentinel; 173 | } 174 | 175 | when.unfold(generator, condition, handler, other).then( 176 | fail, 177 | function(e) { 178 | refute.called(handler); 179 | assert.same(e, sentinel); 180 | } 181 | ).ensure(done); 182 | }, 183 | 184 | 'should reject when transform throws': function(done) { 185 | function condition() { 186 | return false; 187 | } 188 | 189 | function transform() { 190 | throw sentinel; 191 | } 192 | 193 | function generator() { 194 | return [other, other]; 195 | } 196 | 197 | when.unfold(generator, condition, transform, other).then( 198 | fail, 199 | function(e) { 200 | assert.same(e, sentinel); 201 | } 202 | ).ensure(done); 203 | } 204 | }, 205 | 206 | 'iterate': { 207 | 'should invoke condition first': function() { 208 | var called = false; 209 | 210 | return when.iterate(function(x) { 211 | assert(called); 212 | return x; 213 | }, function() { 214 | refute(called); 215 | called = true; 216 | return true; 217 | }, function(x) { 218 | assert(called); 219 | return x; 220 | }, 0).then(function() { 221 | assert(called); 222 | }); 223 | }, 224 | 225 | 'should return a promise for ultimate result': function() { 226 | return when.iterate(function(x) { 227 | return x+1; 228 | }, function(x) { 229 | return x >= 10; 230 | }, function(x) { 231 | return x; 232 | }, 0).then(function(x) { 233 | assert.equals(x, 10); 234 | }); 235 | } 236 | } 237 | }); 238 | -------------------------------------------------------------------------------- /test/join-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when, resolved, rejected; 6 | 7 | when = require('../when'); 8 | resolved = when.resolve; 9 | rejected = when.reject; 10 | 11 | buster.testCase('when.join', { 12 | 13 | 'should resolve empty input': function(done) { 14 | return when.join().then( 15 | function(result) { 16 | assert.equals(result, []); 17 | }, 18 | fail 19 | ).ensure(done); 20 | }, 21 | 22 | 'should join values': function(done) { 23 | when.join(1, 2, 3).then( 24 | function(results) { 25 | assert.equals(results, [1, 2, 3]); 26 | }, 27 | fail 28 | ).ensure(done); 29 | }, 30 | 31 | 'should join promises array': function(done) { 32 | when.join(resolved(1), resolved(2), resolved(3)).then( 33 | function(results) { 34 | assert.equals(results, [1, 2, 3]); 35 | }, 36 | fail 37 | ).ensure(done); 38 | }, 39 | 40 | 'should join mixed array': function(done) { 41 | when.join(resolved(1), 2, resolved(3), 4).then( 42 | function(results) { 43 | assert.equals(results, [1, 2, 3, 4]); 44 | }, 45 | fail 46 | ).ensure(done); 47 | }, 48 | 49 | 'should reject if any input promise rejects': function(done) { 50 | when.join(resolved(1), rejected(2), resolved(3)).then( 51 | fail, 52 | function(failed) { 53 | assert.equals(failed, 2); 54 | } 55 | ).ensure(done); 56 | } 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/keys-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var resolve = when.resolve; 7 | var reject = when.reject; 8 | 9 | var keys = require('../keys'); 10 | 11 | var sentinel = {}; 12 | 13 | function assertNoKeys(object) { 14 | var key, count = 0; 15 | for(key in object) { 16 | if(object.hasOwnProperty(key)) { 17 | count++; 18 | } 19 | } 20 | assert.equals(count, 0); 21 | } 22 | 23 | buster.testCase('when/keys', { 24 | 25 | 'all': { 26 | 'should resolve empty input': function() { 27 | return keys.all({}).then(assertNoKeys); 28 | }, 29 | 30 | 'should resolve input values': function(done) { 31 | var input = { a: 1, b: 2, c: 3 }; 32 | keys.all(input).then( 33 | function(results) { 34 | assert.equals(results, input); 35 | }, 36 | fail 37 | ).ensure(done); 38 | }, 39 | 40 | 'should resolve promised keys': function(done) { 41 | var input = { a: resolve(1), b: 2, c: resolve(3) }; 42 | keys.all(input).then( 43 | function(results) { 44 | assert.equals(results, { a: 1, b: 2, c: 3 }); 45 | }, 46 | fail 47 | ).ensure(done); 48 | }, 49 | 50 | 'should resolve promise for keys': function(done) { 51 | var input = { a: resolve(1), b: 2, c: resolve(3) }; 52 | keys.all(resolve(input)).then( 53 | function(results) { 54 | assert.equals(results, { a: 1, b: 2, c: 3 }); 55 | }, 56 | fail 57 | ).ensure(done); 58 | }, 59 | 60 | 'should reject if key rejects': function(done) { 61 | var input = { a: 1, b: reject(sentinel), c: 3 }; 62 | keys.all(input).then( 63 | fail, 64 | function(e) { 65 | assert.same(e, sentinel); 66 | } 67 | ).ensure(done); 68 | }, 69 | 70 | 'should reject if input promise rejects': function(done) { 71 | keys.all(reject(sentinel)).then( 72 | fail, 73 | function(e) { 74 | assert.same(e, sentinel); 75 | } 76 | ).ensure(done); 77 | } 78 | 79 | }, 80 | 81 | 'map': { 82 | 'should pass key as second param': function() { 83 | var input = { a:1, b:2, c:3 }; 84 | return keys.map(input, function(x, k) { 85 | assert(typeof k === 'string' && input.hasOwnProperty(k)); 86 | return x; 87 | }); 88 | 89 | }, 90 | 91 | 'should resolve empty input': function() { 92 | return keys.map({}).then(assertNoKeys); 93 | }, 94 | 95 | 'should map keys': function(done) { 96 | var input = { a: 1, b: 2, c: 3 }; 97 | keys.map(input, function(x) { 98 | return x + 1; 99 | }).then( 100 | function(results) { 101 | assert.equals(results, { a: 2, b: 3, c: 4 }); 102 | }, 103 | fail 104 | ).ensure(done); 105 | }, 106 | 107 | 'should map promised keys': function(done) { 108 | var input = { a: resolve(1), b: 2, c: resolve(3) }; 109 | keys.map(input, function(x) { 110 | return x + 1; 111 | }).then( 112 | function(results) { 113 | assert.equals(results, { a: 2, b: 3, c: 4 }); 114 | }, 115 | fail 116 | ).ensure(done); 117 | }, 118 | 119 | 'should map promise for keys': function(done) { 120 | var input = { a: resolve(1), b: 2, c: resolve(3) }; 121 | keys.map(resolve(input), function(x) { 122 | return x + 1; 123 | }).then( 124 | function(results) { 125 | assert.equals(results, { a: 2, b: 3, c: 4 }); 126 | }, 127 | fail 128 | ).ensure(done); 129 | }, 130 | 131 | 'should reject if key rejects': function(done) { 132 | var input = { a: 1, b: reject(sentinel), c: 3 }; 133 | keys.map(input, function(x) { 134 | return x + 1; 135 | }).then( 136 | fail, 137 | function(e) { 138 | assert.same(e, sentinel); 139 | } 140 | ).ensure(done); 141 | }, 142 | 143 | 'should reject if input promise rejects': function(done) { 144 | keys.map(reject(sentinel), function(x) { 145 | return x + 1; 146 | }).then( 147 | fail, 148 | function(e) { 149 | assert.same(e, sentinel); 150 | } 151 | ).ensure(done); 152 | }, 153 | 154 | 'should reject if reduceFunc rejects': function(done) { 155 | var input = { a: 1, b: 2, c: 3 }; 156 | keys.map(input, function() { 157 | return reject(sentinel); 158 | }, 0).then( 159 | fail, 160 | function(e) { 161 | assert.same(e, sentinel); 162 | } 163 | ).ensure(done); 164 | }, 165 | 166 | 'should reject if reduceFunc throws': function(done) { 167 | var input = { a: 1, b: 2, c: 3 }; 168 | keys.map(input, function() { 169 | throw sentinel; 170 | }, 0).then( 171 | fail, 172 | function(e) { 173 | assert.same(e, sentinel); 174 | } 175 | ).ensure(done); 176 | } 177 | }, 178 | 179 | 'settle': { 180 | 'should resolve empty input': function() { 181 | return keys.settle({}).then(assertNoKeys); 182 | }, 183 | 184 | 'should resolve promise for keys': function(done) { 185 | var input = { a:1, b:2, c:3 }; 186 | return keys.settle(input).then( 187 | function(results) { 188 | assert.equals( 189 | results, 190 | { 191 | a: { state: 'fulfilled', value: 1 }, 192 | b: { state: 'fulfilled', value: 2 }, 193 | c: { state: 'fulfilled', value: 3 } 194 | } 195 | ); 196 | }, 197 | fail 198 | ).ensure(done); 199 | }, 200 | 201 | 'should resolve promised keys': function(done) { 202 | var input = { a: resolve(1), b: 2, c: resolve(3) }; 203 | keys.settle(input).then( 204 | function(results) { 205 | assert.equals( 206 | results, 207 | { 208 | a: { state: 'fulfilled', value: 1 }, 209 | b: { state: 'fulfilled', value: 2 }, 210 | c: { state: 'fulfilled', value: 3 } 211 | } 212 | ); 213 | }, 214 | fail 215 | ).ensure(done); 216 | }, 217 | 218 | 'should not reject if key rejects': function(done) { 219 | var input = { a: 1, b: reject('reason'), c: 3 }; 220 | keys.settle(input).then( 221 | function(results) { 222 | assert.equals( 223 | results, 224 | { 225 | a: { state: 'fulfilled', value: 1 }, 226 | b: { state: 'rejected', reason: 'reason' }, 227 | c: { state: 'fulfilled', value: 3 } 228 | } 229 | ); 230 | }, 231 | fail 232 | ).ensure(done); 233 | } 234 | } 235 | }); 236 | -------------------------------------------------------------------------------- /test/liftAll-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | 5 | var liftAll = require('../lib/liftAll'); 6 | 7 | var sentinel = {}; 8 | 9 | 10 | function lift(f) { 11 | return function() { 12 | return f.apply(this, arguments); 13 | }; 14 | } 15 | 16 | buster.testCase('when/lib/liftAll', { 17 | 18 | 'should call lift for src own methods': function() { 19 | var src = { a: this.spy(), b: this.spy() }; 20 | var dst = liftAll(lift, void 0, void 0, src); 21 | 22 | dst.a(1); 23 | dst.b(2); 24 | 25 | assert.calledOnceWith(src.a, 1); 26 | assert.calledOnceWith(src.b, 2); 27 | }, 28 | 29 | 'should not call lift for non-functions': function() { 30 | var src = { a: this.spy(), c: sentinel }; 31 | var dst = liftAll(lift, void 0, void 0, src); 32 | 33 | assert.same(dst.c, sentinel); 34 | }, 35 | 36 | 'when dst not provided': { 37 | 'and src is an object': { 38 | 'should lift onto Object.create(src)': function() { 39 | var src = { a: this.spy(), b: sentinel }; 40 | var dst = liftAll(lift, void 0, void 0, src); 41 | 42 | refute.same(src, dst); 43 | assert.isObject(dst); 44 | assert.same(dst.b, sentinel); 45 | assert(dst.hasOwnProperty('a')); 46 | refute(dst.hasOwnProperty('b')); 47 | } 48 | }, 49 | 'and src is a function': { 50 | 'should lift onto a "copy" of src': function() { 51 | var src = this.spy(); 52 | src.a = this.spy(); 53 | var dst = liftAll(lift, void 0, void 0, src); 54 | 55 | refute.same(src, dst); 56 | assert.isFunction(dst); 57 | assert(dst.hasOwnProperty('a')); 58 | 59 | dst.a(); 60 | assert.calledOnce(src.a); 61 | } 62 | } 63 | }, 64 | 65 | 'when dst is provided': { 66 | 'when dst is an object': { 67 | 'should lift onto dst': function() { 68 | var src = { a: function(){}, b: sentinel }; 69 | var d = {}; 70 | var dst = liftAll(lift, void 0, d, src); 71 | 72 | assert.same(dst, d); 73 | } 74 | }, 75 | 'when dst is a function': { 76 | 'should lift onto dst': function() { 77 | var src = { a: function(){}, b: sentinel }; 78 | var d = function(){}; 79 | var dst = liftAll(lift, void 0, d, src); 80 | 81 | assert.same(dst, d); 82 | } 83 | } 84 | }, 85 | 86 | 'when combine is provided': { 87 | 'should call combine for all src own methods': function() { 88 | function addKey(o, f, k) { 89 | o[k+'Test'] = f; 90 | return o; 91 | } 92 | 93 | var src = { a: this.spy(), b: this.spy() }; 94 | var dst = liftAll(lift, addKey, void 0, src); 95 | 96 | dst.aTest(1); 97 | assert.calledOnceWith(src.a, 1); 98 | assert.isFunction(dst.a); 99 | refute(dst.hasOwnProperty('a')); 100 | 101 | dst.bTest(2); 102 | assert.calledOnceWith(src.b, 2); 103 | assert.isFunction(dst.b); 104 | refute(dst.hasOwnProperty('b')); 105 | } 106 | } 107 | 108 | }); 109 | -------------------------------------------------------------------------------- /test/map-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var resolved = when.resolve; 7 | var reject = when.reject; 8 | 9 | /*global setInterval,clearInterval*/ 10 | 11 | function mapper(val) { 12 | return val * 2; 13 | } 14 | 15 | function deferredMapper(val) { 16 | return when(mapper(val)).delay(Math.random()*10); 17 | } 18 | 19 | function identity(x) { 20 | return x; 21 | } 22 | 23 | buster.testCase('when.map', { 24 | 25 | 'should pass index to predicate as second param': function() { 26 | return when.map(['a','b','c'], function(x, i) { 27 | assert(typeof i === 'number'); 28 | return x; 29 | }); 30 | }, 31 | 32 | 'should map input values array': function(done) { 33 | var input = [1, 2, 3]; 34 | when.map(input, mapper).then( 35 | function(results) { 36 | assert.equals(results, [2,4,6]); 37 | }, 38 | fail 39 | ).ensure(done); 40 | }, 41 | 42 | 'should map input promises array': function(done) { 43 | var input = [resolved(1), resolved(2), resolved(3)]; 44 | when.map(input, mapper).then( 45 | function(results) { 46 | assert.equals(results, [2,4,6]); 47 | }, 48 | fail 49 | ).ensure(done); 50 | }, 51 | 52 | 'should map mixed input array': function(done) { 53 | var input = [1, resolved(2), 3]; 54 | when.map(input, mapper).then( 55 | function(results) { 56 | assert.equals(results, [2,4,6]); 57 | }, 58 | fail 59 | ).ensure(done); 60 | }, 61 | 62 | 'should map input when mapper returns a promise': function(done) { 63 | var input = [1,2,3]; 64 | when.map(input, deferredMapper).then( 65 | function(results) { 66 | assert.equals(results, [2,4,6]); 67 | }, 68 | fail 69 | ).ensure(done); 70 | }, 71 | 72 | 'should accept a promise for an array': function(done) { 73 | when.map(resolved([1, resolved(2), 3]), mapper).then( 74 | function(result) { 75 | assert.equals(result, [2,4,6]); 76 | }, 77 | fail 78 | ).ensure(done); 79 | }, 80 | 81 | 'should resolve to empty array when input promise does not resolve to an array': function(done) { 82 | when.map(resolved(123), mapper).then( 83 | function(result) { 84 | assert.equals(result, []); 85 | }, 86 | fail 87 | ).ensure(done); 88 | }, 89 | 90 | 'should map input promises when mapper returns a promise': function(done) { 91 | var input = [resolved(1),resolved(2),resolved(3)]; 92 | when.map(input, mapper).then( 93 | function(results) { 94 | assert.equals(results, [2,4,6]); 95 | }, 96 | fail 97 | ).ensure(done); 98 | }, 99 | 100 | 'should reject when input contains rejection': function(done) { 101 | var input = [resolved(1), reject(2), resolved(3)]; 102 | when.map(input, mapper).then( 103 | fail, 104 | function(result) { 105 | assert.equals(result, 2); 106 | } 107 | ).ensure(done); 108 | }, 109 | 110 | 'should propagate progress': function() { 111 | // Thanks @depeele for this test 112 | var input = [_resolver(1), _resolver(2), _resolver(3)]; 113 | var ncall = 0; 114 | 115 | return when.map(input, identity).then( 116 | function() { 117 | assert.equals(ncall, 6); 118 | }, 119 | fail, 120 | function() { 121 | ncall++; 122 | } 123 | ); 124 | 125 | function _resolver(id) { 126 | return when.promise(function(resolve, reject, notify) { 127 | var loop = 0; 128 | var timer = setInterval(function () { 129 | notify(id); 130 | loop++; 131 | if (loop === 2) { 132 | clearInterval(timer); 133 | resolve(id); 134 | } 135 | }, 1); 136 | }); 137 | } 138 | } 139 | }); 140 | -------------------------------------------------------------------------------- /test/monitor/PromiseMonitor-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var Promise = require('../../lib/Promise'); 6 | var PromiseMonitor = require('../../monitor/PromiseMonitor'); 7 | 8 | buster.testCase('when/monitor/PromiseMonitor', { 9 | 10 | 'should call reporter.configurePromiseMonitor with self': function() { 11 | var spy = this.spy(); 12 | var m = new PromiseMonitor({ 13 | configurePromiseMonitor: spy 14 | }); 15 | 16 | assert.calledOnceWith(spy, m); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /test/monitor/all.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var when = require('../../when'); 17 | 18 | var p = when.reject(new Error('fail1')); 19 | 20 | when.all([p]); 21 | 22 | }); 23 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 24 | 25 | -------------------------------------------------------------------------------- /test/monitor/deep-chain.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | var Promise = require('../../when').Promise; 16 | 17 | function f1() { 18 | return Promise.resolve(123); 19 | } 20 | 21 | console.log('*** Creating deep promise rejection chain ***'); 22 | var p = f1(); 23 | 24 | p = p.then(ok); 25 | 26 | p = p.then(ok); 27 | 28 | // Cause an unhandled rejection deep in the promise chain 29 | // It's unhandled because after this statement, p is a 30 | // rejected promise but has no onRejected handler 31 | // This should be logged 32 | p = p.then(reject); 33 | 34 | // Some time later, handle the rejection 35 | // When this happens, p suddenly becomes handled (obviously!), 36 | // and this will be logged as well. 37 | setTimeout(function() { 38 | console.log('*** handling rejection ***'); 39 | // p.done(); 40 | p.catch(ok); 41 | }, 1337); 42 | 43 | function ok(x) { 44 | return x; 45 | } 46 | 47 | function reject(x) { 48 | return Promise.reject(new Error('error originates here')); 49 | } 50 | }); 51 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 52 | 53 | -------------------------------------------------------------------------------- /test/monitor/delay-handled.js: -------------------------------------------------------------------------------- 1 | (function(define) { 'use strict'; 2 | define(function(require) { 3 | 4 | var when = require('../../when'); 5 | var async = require('../../lib/env').asap; 6 | 7 | var p = when.reject(new Error('TEST FAILED, should not see this')); 8 | 9 | async(function() { 10 | p.catch(function(){}); 11 | }); 12 | }); 13 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 14 | 15 | -------------------------------------------------------------------------------- /test/monitor/developer-error.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var Promise = require('../../when').Promise; 17 | 18 | var p = Promise.resolve(123); 19 | 20 | p.then(function() { 21 | oops(); 22 | }); 23 | 24 | function infiniteRecursion() { 25 | infiniteRecursion(); 26 | } 27 | 28 | p.then(infiniteRecursion); 29 | 30 | var notAFunction = {}; 31 | function tryToCallNotAFunction() { 32 | notAFunction(); 33 | } 34 | 35 | p.then(tryToCallNotAFunction); 36 | }); 37 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 38 | 39 | -------------------------------------------------------------------------------- /test/monitor/done.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | var Promise = require('../../when').Promise; 16 | 17 | Promise.resolve(123) 18 | .then(function(x) { 19 | // throw x; 20 | throw new Error(x); 21 | // return Promise.reject(x); 22 | // foo(); 23 | // throw new TypeError(x); 24 | }) 25 | // .then(void 0, function() { console.log(123);}) 26 | .done(console.log); 27 | 28 | }); 29 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/monitor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/monitor/lift-node.js: -------------------------------------------------------------------------------- 1 | //require('../../monitor/console'); 2 | var node = require('../../node'); 3 | 4 | function test(cb) { 5 | throw new Error('fail'); 6 | // cb(new Error('fail')); 7 | } 8 | 9 | var f = node.lift(test); 10 | 11 | f(); -------------------------------------------------------------------------------- /test/monitor/map.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var when = require('../../when'); 17 | 18 | var p = when.reject(new Error('fail1')); 19 | 20 | // when.map(p, function(x){return x;}); 21 | when.map([p], function(x){return x;}); 22 | // when.map([123], fail); 23 | 24 | function fail(x){ 25 | throw new Error('map failed'); 26 | } 27 | 28 | }); 29 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 30 | 31 | -------------------------------------------------------------------------------- /test/monitor/multiple-escapes.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var Promise = require('../../when').Promise; 17 | 18 | var i = 1; 19 | var p = new Promise(function(_, reject) { 20 | setTimeout(reject.bind(null, new Error(i++)), 1); 21 | }); 22 | 23 | var p = new Promise(function(_, reject) { 24 | setTimeout(reject.bind(null, new Error(i++)), 1); 25 | }); 26 | 27 | var p = new Promise(function(_, reject) { 28 | setTimeout(reject.bind(null, new Error(i++)), 1); 29 | }); 30 | 31 | }); 32 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 33 | 34 | -------------------------------------------------------------------------------- /test/monitor/unhandled-begets-unhandled.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var Promise = require('../../when').Promise; 17 | // var Promise = require('../../es6-shim/Promise'); 18 | 19 | var p = new Promise.reject(new Error('first error')); 20 | 21 | setTimeout(function() { 22 | // console.log('***Begetting new unhandled error now***'); 23 | p['catch'](function() { 24 | throw new Error('unhandled-begets-unhandled'); 25 | }); 26 | }, 2000); 27 | 28 | }); 29 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 30 | -------------------------------------------------------------------------------- /test/monitor/unhandled-forever.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var Promise = require('../../when').Promise; 17 | 18 | function f1() { 19 | return new Promise(function(_, reject) { 20 | reject(new Error('unhandled-forever')); 21 | }); 22 | } 23 | 24 | function f2(p) { 25 | return p.then(function() {}); 26 | } 27 | 28 | function f3(p) { 29 | return p.then(function() {}); 30 | } 31 | 32 | // f1(); 33 | // f2(f1()); 34 | f3(f2(f1())); 35 | }); 36 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 37 | 38 | -------------------------------------------------------------------------------- /test/monitor/unhandled-handled-later.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | 3 | /** 4 | * Licensed under the MIT License at: 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * 7 | * @author: Brian Cavalier 8 | * @author: John Hann 9 | */ 10 | 11 | (function(define) { 'use strict'; 12 | define(function(require) { 13 | 14 | // require('../../monitor/console'); 15 | 16 | var Promise = require('../../when').Promise; 17 | // var Promise = require('bluebird'); 18 | 19 | function run() { 20 | var p = new Promise(function(_, reject) { 21 | reject(new Error('unhandled-handled-later')); 22 | }); 23 | 24 | setTimeout(function() { 25 | // console.log('***Handling error now***'); 26 | p['catch'](function() { /* handled by squelching */ }); 27 | }, 1000); 28 | } 29 | 30 | run(); 31 | // run(); 32 | // run(); 33 | // run(); 34 | // run(); 35 | // run(); 36 | // run(); 37 | 38 | }); 39 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 40 | 41 | -------------------------------------------------------------------------------- /test/monitor/unhandledRejectionApi-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var Promise = require('../../lib/Promise'); 6 | 7 | function replace(target, method, replacement) { 8 | var tmp = target[method]; 9 | target[method] = function() { 10 | target[method] = tmp; 11 | return replacement.apply(this, arguments); 12 | }; 13 | } 14 | 15 | buster.testCase('when/unhandledRejectionApi', { 16 | 17 | 'reject should trigger report': function(done) { 18 | replace(Promise, 'onPotentiallyUnhandledRejection', function () { 19 | assert(true); 20 | done(); 21 | }); 22 | 23 | new Promise(function (_, reject) { 24 | reject(); 25 | }); 26 | }, 27 | 28 | 'Promise.reject should trigger report': function(done) { 29 | replace(Promise, 'onPotentiallyUnhandledRejection', function () { 30 | assert(true); 31 | done(); 32 | }); 33 | 34 | Promise.reject(); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /test/parallel-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var when = require('../when'); 5 | var parallel = require('../parallel'); 6 | 7 | function createTask(y) { 8 | return function() { 9 | return y; 10 | }; 11 | } 12 | 13 | function expectArgs(expected) { 14 | return function() { 15 | var args = Array.prototype.slice.call(arguments); 16 | assert.equals(args, expected); 17 | }; 18 | } 19 | 20 | buster.testCase('when/parallel', { 21 | 22 | 'should execute all tasks': function() { 23 | return parallel([createTask(1), createTask(2), createTask(3)]).then( 24 | function(result) { 25 | assert.equals(result, [1, 2, 3]); 26 | } 27 | ); 28 | }, 29 | 30 | 'should resolve to empty array when no tasks supplied': function() { 31 | return parallel([], 1, 2, 3).then( 32 | function(result) { 33 | assert.equals(result, []); 34 | } 35 | ); 36 | }, 37 | 38 | 'should pass args to all tasks': function(done) { 39 | var expected, tasks; 40 | 41 | expected = [1, 2, 3]; 42 | tasks = [expectArgs(expected), expectArgs(expected), expectArgs(expected)]; 43 | 44 | parallel.apply(void 0, [tasks].concat(expected)).ensure(done); 45 | }, 46 | 47 | 'should accept promises for args': function(done) { 48 | var expected, tasks; 49 | 50 | expected = [1, 2, 3]; 51 | tasks = [expectArgs(expected), expectArgs(expected), expectArgs(expected)]; 52 | 53 | parallel.apply(void 0, [tasks].concat(expected.map(when))).ensure(done); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /test/pipeline-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var when = require('../when'); 5 | var pipeline = require('../pipeline'); 6 | 7 | function createTask(y) { 8 | return function(x) { 9 | return x + y; 10 | }; 11 | } 12 | 13 | buster.testCase('when/pipeline', { 14 | 15 | 'should execute tasks in order': function() { 16 | return pipeline([createTask('b'), createTask('c'), createTask('d')], 'a').then( 17 | function(result) { 18 | assert.equals(result, 'abcd'); 19 | } 20 | ); 21 | }, 22 | 23 | 'should resolve to initial args when no tasks supplied': function() { 24 | return pipeline([], 'a', 'b').then( 25 | function(result) { 26 | assert.equals(result, ['a', 'b']); 27 | } 28 | ); 29 | }, 30 | 31 | 'should resolve to empty array when no tasks and no args supplied': function() { 32 | return pipeline([]).then( 33 | function(result) { 34 | assert.equals(result, []); 35 | } 36 | ); 37 | }, 38 | 39 | 'should pass args to initial task': function() { 40 | var expected, tasks; 41 | 42 | expected = [1, 2, 3]; 43 | tasks = [this.spy()]; 44 | 45 | return pipeline.apply(null, [tasks].concat(expected)).then( 46 | function() { 47 | assert.calledOnceWith.apply(assert, tasks.concat(expected)); 48 | } 49 | ); 50 | }, 51 | 52 | 'should allow initial args to be promises': function() { 53 | var expected, tasks; 54 | 55 | expected = [1, 2, 3]; 56 | tasks = [this.spy()]; 57 | 58 | return pipeline.apply(null, [tasks].concat([when(1), when(2), when(3)])).then( 59 | function() { 60 | assert.calledOnceWith.apply(assert, tasks.concat(expected)); 61 | } 62 | ); 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /test/poll-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var poll = require('../poll'); 7 | 8 | function failIfCalled(done, message) { 9 | return function () { 10 | fail(message || 'should never be called'); 11 | done(); 12 | }; 13 | } 14 | 15 | buster.testCase('when/poll', { 16 | 17 | 'should poll until canceled': function (done) { 18 | var i, p, progback; 19 | 20 | i = 0; 21 | p = poll(function () { i += 1; return i; }, 10); 22 | progback = this.spy(function (result) { assert.equals(i, result); }); 23 | 24 | p.then( 25 | failIfCalled(done, 'should never be resolved'), 26 | function () { 27 | assert(progback.called); 28 | done(); 29 | }, 30 | progback 31 | ); 32 | when().delay(100).then(p.cancel); 33 | }, 34 | 35 | 'should poll with interval function': function (done) { 36 | var countdown, interval; 37 | 38 | countdown = 3; 39 | interval = this.spy(function () { 40 | return when().delay(10); 41 | }); 42 | 43 | poll(function () {}, interval, function () { countdown -= 1; return countdown === 0; }).then( 44 | function () { 45 | assert(interval.calledTwice); 46 | done(); 47 | }, 48 | failIfCalled(done, 'should never be rejected') 49 | ); 50 | }, 51 | 52 | 'should be canceled by rejected work': function (done) { 53 | var p = poll(when.reject, 10); 54 | 55 | p.then( 56 | failIfCalled(done, 'should never be resolved'), 57 | function () { 58 | assert(true); 59 | done(); 60 | }, 61 | failIfCalled(done, 'should never receive progress') 62 | ); 63 | }, 64 | 65 | 'should poll with delayed start': function (done) { 66 | var i, p, progback; 67 | 68 | i = 0; 69 | p = poll(function () { i += 1; return i; }, 10, function (result) { return result === 2; }, true); 70 | progback = this.spy(function (result) { assert.equals(result, 1); }); 71 | 72 | p.then( 73 | function (result) { 74 | assert.equals(result, 2); 75 | assert(progback.called); 76 | done(); 77 | }, 78 | failIfCalled(done), 79 | progback 80 | ); 81 | }, 82 | 83 | 'should keep polling from rejected verification, stop for resolved verification': function (done) { 84 | var i, p, progback; 85 | 86 | i = 0; 87 | p = poll(function () { 88 | i += 1; return i; 89 | }, 10, function () { 90 | return i < 3 ? when.reject() : when.resolve(true); 91 | }); 92 | progback = this.spy(function (result) { 93 | assert.equals(result, i); 94 | }); 95 | 96 | p.then( 97 | function (result) { 98 | assert.equals(result, 3); 99 | assert(progback.calledTwice); 100 | done(); 101 | }, 102 | failIfCalled(done, 'should never be rejected'), 103 | progback 104 | ); 105 | }, 106 | 107 | 'should keep polling from falsey resolved verification, stop for truthy resolved verification': function (done) { 108 | var i, p, progback; 109 | 110 | i = 0; 111 | p = poll(function () { i += 1; return i; }, 10, function (result) { return result < 3 ? when.resolve(false) : when.resolve(true); }); 112 | progback = this.spy(function (result) { assert.equals(result, i); }); 113 | 114 | p.then( 115 | function (result) { 116 | assert.equals(result, 3); 117 | assert(progback.calledTwice); 118 | done(); 119 | }, 120 | failIfCalled(done, 'should never be rejected'), 121 | progback 122 | ); 123 | } 124 | 125 | }); 126 | -------------------------------------------------------------------------------- /test/promises-aplus-adapter.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | if(typeof exports === 'object') { 5 | 6 | var when = require('../when'); 7 | 8 | // Silence potentially unhandled rejections 9 | when.Promise.onPotentiallyUnhandledRejection = function() {}; 10 | 11 | exports.resolved = when.resolve; 12 | exports.rejected = when.reject; 13 | exports.deferred = when.defer; 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /test/race-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var CorePromise = require('../lib/Promise'); 6 | 7 | var sentinel = { value: 'sentinel' }; 8 | var fulfilled = CorePromise.resolve(sentinel); 9 | var never = CorePromise.never(); 10 | 11 | function delayReject(ms) { 12 | /*global setTimeout*/ 13 | return new CorePromise(function(resolve, reject) { 14 | setTimeout(function() { 15 | reject(sentinel); 16 | }, ms); 17 | }); 18 | } 19 | 20 | buster.testCase('CorePromise.race', { 21 | 'should return empty race for length 0': function() { 22 | assert.equals(never, CorePromise.race([])); 23 | }, 24 | 25 | 'should reject with TypeError when passed a non-iterable (array in es5)': function() { 26 | return CorePromise.race(null).then(fail, function(e) { 27 | assert(e instanceof TypeError); 28 | }); 29 | }, 30 | 31 | 'should be identity for length 1': { 32 | 'when fulfilled with value': function() { 33 | return CorePromise.race([sentinel]).then(function(x) { 34 | assert.same(x, sentinel); 35 | }); 36 | }, 37 | 38 | 'when fulfilled via promise': function() { 39 | return CorePromise.race([fulfilled]).then(function(x) { 40 | assert.same(x, sentinel); 41 | }); 42 | }, 43 | 44 | 'when rejected': function() { 45 | var rejected = CorePromise.reject(sentinel); 46 | return CorePromise.race([rejected]) 47 | .then(void 0, function(x) { 48 | assert.same(x, sentinel); 49 | }); 50 | } 51 | }, 52 | 53 | 'should be commutative': { 54 | 'when fulfilled': function() { 55 | return CorePromise.race([fulfilled, never]).then(function(x) { 56 | return CorePromise.race([never, fulfilled]).then(function(y) { 57 | assert.same(x, y); 58 | }); 59 | }); 60 | }, 61 | 62 | 'when rejected': function() { 63 | var rejected = CorePromise.reject(sentinel); 64 | return CorePromise.race([rejected, never]).then(void 0, function(x) { 65 | return CorePromise.race([never, rejected]).then(void 0, function(y) { 66 | assert.same(x, y); 67 | }); 68 | }); 69 | } 70 | }, 71 | 72 | 'should fulfill when winner fulfills': function() { 73 | return CorePromise.race([delayReject(1), delayReject(1), fulfilled]) 74 | .then(function(x) { 75 | assert.same(x, sentinel); 76 | }, fail); 77 | }, 78 | 79 | 'should reject when winner rejects': function() { 80 | var rejected = CorePromise.reject(sentinel); 81 | return CorePromise.race([fulfilled.delay(1), fulfilled.delay(1), rejected]) 82 | .then(fail, function(x) { 83 | assert.same(x, sentinel); 84 | }); 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /test/reject-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | 7 | buster.testCase('when.reject', { 8 | 9 | 'should reject an immediate value': function(done) { 10 | var expected = 123; 11 | 12 | when.reject(expected).then( 13 | fail, 14 | function(value) { 15 | assert.equals(value, expected); 16 | } 17 | ).ensure(done); 18 | }, 19 | 20 | 'should reject a resolved promise': function(done) { 21 | var expected, d; 22 | 23 | expected = 123; 24 | d = when.defer(); 25 | d.resolve(expected); 26 | 27 | when.reject(d.promise).then( 28 | fail, 29 | function(value) { 30 | assert.same(value, d.promise); 31 | } 32 | ).ensure(done); 33 | }, 34 | 35 | 'should reject a rejected promise': function(done) { 36 | var expected, d; 37 | 38 | expected = 123; 39 | d = when.defer(); 40 | d.reject(expected); 41 | 42 | when.reject(d.promise).then( 43 | fail, 44 | function(value) { 45 | assert.equals(value, d.promise); 46 | } 47 | ).ensure(done); 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /test/resolve-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | var sentinel = { value: 'sentinel' }; 7 | var other = { value: 'other' }; 8 | 9 | function hasGetters() { 10 | try { 11 | return Object.defineProperty({}, 'a', { get: function() { return 1; } }).a === 1; 12 | } catch (ex) {} 13 | } 14 | 15 | buster.testCase('when.resolve', { 16 | 17 | 'should resolve an immediate value': function(done) { 18 | var expected = 123; 19 | 20 | when.resolve(expected).then( 21 | function(value) { 22 | assert.equals(value, expected); 23 | }, 24 | fail 25 | ).ensure(done); 26 | }, 27 | 28 | 'should resolve a resolved promise': function(done) { 29 | var expected, d; 30 | 31 | expected = 123; 32 | d = when.defer(); 33 | d.resolve(expected); 34 | 35 | when.resolve(d.promise).then( 36 | function(value) { 37 | assert.equals(value, expected); 38 | }, 39 | fail 40 | ).ensure(done); 41 | }, 42 | 43 | 'should reject a rejected promise': function(done) { 44 | var expected, d; 45 | 46 | expected = 123; 47 | d = when.defer(); 48 | d.reject(expected); 49 | 50 | when.resolve(d.promise).then( 51 | fail, 52 | function(value) { 53 | assert.equals(value, expected); 54 | } 55 | ).ensure(done); 56 | }, 57 | 58 | 'should preserve implicit order': function() { 59 | var result = []; 60 | var resolveA; 61 | var a = new when.Promise(function() { 62 | resolveA = arguments[0]; 63 | }); 64 | 65 | var c = a.then(function() { 66 | result.push(1); 67 | }); 68 | 69 | var resolveB; 70 | var b = new when.Promise(function() { 71 | resolveB = arguments[0]; 72 | }); 73 | 74 | // There is a very subtle implicit ordering between 75 | // resolving a with b, a's handlers, and b's handlers. 76 | // It seems that a reasonable order should be: 77 | // b handlers run before a handlers added in the same stack 78 | b.then(function() { 79 | result.push(2); 80 | }); 81 | 82 | resolveA(b); 83 | 84 | b.then(function() { 85 | result.push(3); 86 | }); 87 | 88 | resolveB(); 89 | 90 | return c.then(function() { 91 | assert.equals(result, [2,3,1]); 92 | }); 93 | }, 94 | 95 | 'when assimilating untrusted thenables': { 96 | 97 | 'should trap exceptions during assimilation': function(done) { 98 | when.resolve({ 99 | then: function() { 100 | throw sentinel; 101 | } 102 | }).then( 103 | fail, 104 | function(val) { 105 | assert.same(val, sentinel); 106 | } 107 | ).ensure(done); 108 | }, 109 | 110 | 'should ignore exceptions after fulfillment': function(done) { 111 | when.resolve({ 112 | then: function(onFulfilled) { 113 | onFulfilled(sentinel); 114 | throw other; 115 | } 116 | }).then( 117 | function(val) { 118 | assert.same(val, sentinel); 119 | }, 120 | fail 121 | ).ensure(done); 122 | }, 123 | 124 | 'should ignore exceptions after rejection': function(done) { 125 | when.resolve({ 126 | then: function(_, onRejected) { 127 | onRejected(sentinel); 128 | throw other; 129 | } 130 | }).then( 131 | fail, 132 | function(val) { 133 | assert.same(val, sentinel); 134 | } 135 | ).ensure(done); 136 | }, 137 | 138 | 'should assimilate thenable used as fulfillment value': function(done) { 139 | when.resolve({ 140 | then: function(onFulfilled) { 141 | onFulfilled({ 142 | then: function(onFulfilled) { 143 | onFulfilled(sentinel); 144 | } 145 | }); 146 | throw other; 147 | } 148 | }).then( 149 | function(val) { 150 | assert.same(val, sentinel); 151 | }, 152 | fail 153 | ).ensure(done); 154 | }, 155 | 156 | 'should call untrusted then only after stack clears': function(done) { 157 | var value, p, spy; 158 | 159 | // value = intentionally undefined 160 | spy = this.spy(); 161 | 162 | p = when.resolve({ 163 | then: function(fulfill) { 164 | spy(value); 165 | fulfill(); 166 | } 167 | }).then(function() { 168 | assert.calledWith(spy, sentinel); 169 | }).ensure(done); 170 | 171 | value = sentinel; 172 | }, 173 | 174 | 'should assimilate thenables provided as fulfillment arg': function(done) { 175 | when.resolve({ 176 | then: function(fulfill) { 177 | fulfill({ 178 | then: function(fulfill) { 179 | fulfill(sentinel); 180 | } 181 | }); 182 | } 183 | }).then(function(value) { 184 | assert.same(value, sentinel); 185 | }).ensure(done); 186 | }, 187 | 188 | 'should reject if accessing thenable.then throws': function(done) { 189 | var result, thenable; 190 | 191 | if(hasGetters()) { 192 | thenable = {}; 193 | Object.defineProperty(thenable, 'then', { 194 | get: function() { 195 | throw sentinel; 196 | } 197 | }); 198 | 199 | result = when.resolve(thenable).then( 200 | fail, 201 | function(e) { 202 | assert.same(e, sentinel); 203 | } 204 | ).ensure(done); 205 | 206 | } else { 207 | // Non-ES5 env, no need to test pathological getters/proxies 208 | assert(true); 209 | done(); 210 | } 211 | } 212 | 213 | } 214 | 215 | }); 216 | -------------------------------------------------------------------------------- /test/sequence-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var when = require('../when'); 5 | var sequence = require('../sequence'); 6 | 7 | var sentinel = { value: 'sentinel' }; 8 | 9 | function createTask(y) { 10 | return function() { 11 | return y; 12 | }; 13 | } 14 | 15 | function expectArgs(expected) { 16 | return function() { 17 | var args = Array.prototype.slice.call(arguments); 18 | assert.equals(args, expected); 19 | }; 20 | } 21 | 22 | buster.testCase('when/sequence', { 23 | 24 | 'should execute tasks in order': function() { 25 | return sequence([createTask(1), createTask(2), createTask(3)]).then( 26 | function(result) { 27 | assert.equals(result, [1, 2, 3]); 28 | } 29 | ); 30 | }, 31 | 32 | 'should resolve to empty array when no tasks supplied': function() { 33 | return sequence([], 1, 2, 3).then( 34 | function(result) { 35 | assert.equals(result, []); 36 | } 37 | ); 38 | }, 39 | 40 | 'should pass args to all tasks': function(done) { 41 | var expected, tasks; 42 | 43 | expected = [1, 2, 3]; 44 | tasks = [expectArgs(expected), expectArgs(expected), expectArgs(expected)]; 45 | 46 | return sequence.apply(null, [tasks].concat(expected)).ensure(done); 47 | }, 48 | 49 | 'should accept promises for args': function(done) { 50 | var expected, tasks; 51 | 52 | expected = [1, 2, 3]; 53 | tasks = [expectArgs(expected), expectArgs(expected), expectArgs(expected)]; 54 | 55 | expected = [when(1), when(2), when(3)]; 56 | return sequence.apply(null, [tasks].concat(expected)).ensure(done); 57 | }, 58 | 59 | 'should reject if task throws': function() { 60 | return sequence([function () { 61 | return 1; 62 | }, function () { 63 | throw sentinel; 64 | }])['catch'](function (e) { 65 | assert.same(e, sentinel); 66 | }); 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /test/settle-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | var fail = buster.referee.fail; 5 | 6 | var when = require('../when'); 7 | var sentinel = {}; 8 | 9 | function assertFulfilled(s, value) { 10 | assert.equals(s.state, 'fulfilled'); 11 | assert.same(s.value, value); 12 | } 13 | 14 | function assertRejected(s, reason) { 15 | assert.equals(s.state, 'rejected'); 16 | assert.same(s.reason, reason); 17 | } 18 | 19 | buster.testCase('when.settle', { 20 | 'should settle empty array': function() { 21 | return when.settle([]).then(function(settled) { 22 | assert.equals(settled.length, 0); 23 | }); 24 | }, 25 | 26 | 'should reject if promise for input array rejects': function() { 27 | return when.settle(when.reject(sentinel)).then( 28 | fail, 29 | function(reason) { 30 | assert.same(reason, sentinel); 31 | } 32 | ); 33 | }, 34 | 35 | 'should settle values': function() { 36 | var array = [0, 1, sentinel]; 37 | return when.settle(array).then(function(settled) { 38 | assertFulfilled(settled[0], 0); 39 | assertFulfilled(settled[1], 1); 40 | assertFulfilled(settled[2], sentinel); 41 | }); 42 | }, 43 | 44 | 'should settle promises': function() { 45 | var array = [0, when.resolve(sentinel), when.reject(sentinel)]; 46 | return when.settle(array).then(function(settled) { 47 | assertFulfilled(settled[0], 0); 48 | assertFulfilled(settled[1], sentinel); 49 | assertRejected(settled[2], sentinel); 50 | }); 51 | }, 52 | 53 | 'returned promise should fulfill once all inputs settle': function() { 54 | /*global setTimeout*/ 55 | var array, p1, p2, resolve, reject; 56 | 57 | p1 = when.promise(function(r) { resolve = r; }); 58 | p2 = when.promise(function(_, r) { reject = r; }); 59 | 60 | array = [0, p1, p2]; 61 | 62 | setTimeout(function() { resolve(sentinel); }, 0); 63 | setTimeout(function() { reject(sentinel); }, 0); 64 | 65 | return when.settle(array).then(function(settled) { 66 | assertFulfilled(settled[0], 0); 67 | assertFulfilled(settled[1], sentinel); 68 | assertRejected(settled[2], sentinel); 69 | }); 70 | }, 71 | 72 | 'should not report unhandled rejection for rejected inputs': function(done) { 73 | var P = when.Promise; 74 | var spy = P.onPotentiallyUnhandledRejection = this.spy(); 75 | when.settle([when.reject()]); 76 | 77 | setTimeout(function() { 78 | refute.called(spy); 79 | done(); 80 | }, 10); 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /test/some-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var when = require('../when'); 6 | 7 | function contains(array, value) { 8 | for(var i = array.length-1; i >= 0; i--) { 9 | if(array[i] === value) { 10 | return true; 11 | } 12 | } 13 | 14 | return false; 15 | } 16 | 17 | function isSubset(subset, superset) { 18 | var i, subsetLen; 19 | 20 | subsetLen = subset.length; 21 | 22 | if (subsetLen > superset.length) { 23 | return false; 24 | } 25 | 26 | for(i = 0; i number of inputs': { 39 | 'for base case of zero inputs': function() { 40 | return when.some([], 1)['catch']( 41 | function (e) { 42 | assert(e instanceof RangeError); 43 | }); 44 | }, 45 | 46 | 'for m inputs requesting n > m winners': function() { 47 | var input = [1,,2]; 48 | return when.some(input, input.length+1)['catch']( 49 | function (e) { 50 | assert(e instanceof RangeError); 51 | }); 52 | } 53 | }, 54 | 55 | 'when input promise does not resolve to array': function() { 56 | return when.some(when.resolve(1), 1)['catch']( 57 | function(e) { 58 | assert(e instanceof RangeError); 59 | }); 60 | } 61 | }, 62 | 63 | 'should reject with all rejected input values if resolving howMany becomes impossible': function() { 64 | var input = [when.resolve(1), when.reject(2), when.reject(3)]; 65 | return when.some(input, 2).then( 66 | fail, 67 | function(failed) { 68 | assert.equals(failed, [2, 3]); 69 | }); 70 | }, 71 | 72 | 'should resolve values array': function() { 73 | var input = [1, 2, 3]; 74 | return when.some(input, 2).then( 75 | function(results) { 76 | assert(isSubset(results, input)); 77 | }); 78 | }, 79 | 80 | 'should resolve promises array': function() { 81 | var input = [when.resolve(1), when.resolve(2), when.resolve(3)]; 82 | return when.some(input, 2).then( 83 | function(results) { 84 | assert(isSubset(results, [1, 2, 3])); 85 | }); 86 | }, 87 | 88 | 'should resolve sparse array input': function() { 89 | var input = [, 1, , 2, 3 ]; 90 | return when.some(input, 2).then( 91 | function(results) { 92 | assert(isSubset(results, input)); 93 | }); 94 | }, 95 | 96 | 'should accept a promise for an array': function() { 97 | var expected, input; 98 | 99 | expected = [1, 2, 3]; 100 | input = when.resolve(expected); 101 | 102 | return when.some(input, 2).then( 103 | function(results) { 104 | assert.equals(results.length, 2); 105 | }); 106 | }, 107 | 108 | 'should resolve to empty array when n is zero': function() { 109 | return when.some([1,2,3], 0).then(function(result) { 110 | assert.equals(result, []); 111 | }); 112 | } 113 | }); 114 | -------------------------------------------------------------------------------- /test/timeout-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var fail = buster.referee.fail; 4 | 5 | var CorePromise = require('../lib/Promise'); 6 | var TimeoutError = require('../lib/TimeoutError'); 7 | var timeout = require('../timeout'); 8 | 9 | var sentinel = {}; 10 | 11 | function FakePromise() { 12 | this.then = function() { 13 | return this; 14 | }; 15 | } 16 | 17 | buster.testCase('when/timeout', { 18 | 'should reject with TimeoutError': function(done) { 19 | timeout(10, new FakePromise()).then( 20 | fail, 21 | function(e) { 22 | assert(e instanceof TimeoutError); 23 | } 24 | ).ensure(done); 25 | }, 26 | 27 | 'should not timeout when rejected before timeout': function(done) { 28 | timeout(10, CorePromise.reject(sentinel)).then( 29 | fail, 30 | function(val) { 31 | assert.same(val, sentinel); 32 | } 33 | ).ensure(done); 34 | }, 35 | 36 | 'should not timeout when resolved before timeout': function(done) { 37 | timeout(10, CorePromise.resolve(sentinel)).then( 38 | function(val) { 39 | assert.same(val, sentinel); 40 | }, 41 | fail 42 | ).ensure(done); 43 | }, 44 | 45 | 'promise.timeout': { 46 | 47 | 'should reject with TimeoutError': function() { 48 | return CorePromise.never().timeout(0).then( 49 | fail, 50 | function(e) { 51 | assert(e instanceof TimeoutError); 52 | } 53 | ); 54 | }, 55 | 56 | 'should pattern match': function() { 57 | return CorePromise.never().timeout(0).catch(TimeoutError, function(e) { 58 | assert(e instanceof TimeoutError); 59 | }); 60 | }, 61 | 62 | 'should reject after timeout with the provided reason': function() { 63 | return CorePromise.never().timeout(0, sentinel).then( 64 | fail, 65 | function(e) { 66 | assert.same(e, sentinel); 67 | } 68 | ); 69 | }, 70 | 71 | 'should reject after timeout with default reason when undefined': function() { 72 | return CorePromise.never().timeout(0, void 0).then( 73 | fail, 74 | function(e) { 75 | assert(e instanceof TimeoutError); 76 | } 77 | ); 78 | }, 79 | 80 | 'should not timeout when rejected before timeout': function() { 81 | return CorePromise.reject(sentinel).timeout(0).then( 82 | fail, 83 | function(val) { 84 | assert.same(val, sentinel); 85 | } 86 | ); 87 | }, 88 | 89 | 'should not timeout when resolved before timeout': function() { 90 | return CorePromise.resolve(sentinel).timeout(0).then( 91 | function(val) { 92 | assert.same(val, sentinel); 93 | } 94 | ); 95 | }, 96 | 97 | 'should propagate progress': function(done) { 98 | return new CorePromise(function(resolve, _, notify) { 99 | notify(sentinel); 100 | }) 101 | .timeout(10) 102 | .then(void 0, void 0, function(x) { 103 | assert.same(x, sentinel); 104 | done(); 105 | }); 106 | } 107 | 108 | } 109 | }); 110 | -------------------------------------------------------------------------------- /test/unfold/list-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var list = require('../../unfold/list'); 5 | 6 | var sentinel = {}; 7 | 8 | function noop() {} 9 | 10 | buster.testCase('when/unfold/list', { 11 | 12 | 'should produce an empty list when proceed returns truthy immediately': function(done) { 13 | function condition() { 14 | return true; 15 | } 16 | 17 | list(noop, condition, sentinel).then( 18 | function(value) { 19 | assert.equals(value, []); 20 | } 21 | ).ensure(done); 22 | }, 23 | 24 | 'should produce a list of N elements': function(done) { 25 | var len = 3; 26 | 27 | function condition(i) { 28 | return i == len; 29 | } 30 | 31 | function generate(x) { 32 | return [x, x+1]; 33 | } 34 | 35 | list(generate, condition, 0).then( 36 | function(result) { 37 | assert.equals(result.length, len); 38 | assert.equals(result, [0, 1, 2]); 39 | } 40 | ).ensure(done); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /test/unhandledRejection-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var unhandledRejection = require('../lib/decorators/unhandledRejection'); 3 | 4 | buster.testCase('unhandledRejection', { 5 | 6 | 'should not fail if JSON.stringify throws': function() { 7 | var fixture = unhandledRejection({}); 8 | var circle = { self: void 0 }; 9 | circle.self = circle; 10 | 11 | buster.refute.exception(function() { 12 | fixture.onPotentiallyUnhandledRejection({ 13 | id: 'JSON.stringify circular ref test', 14 | handled: false, 15 | value: circle 16 | }); 17 | }); 18 | } 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /test/when-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | var fail = buster.referee.fail; 5 | 6 | var when = require('../when'); 7 | 8 | function identity(val) { return val; } 9 | function constant(val) { return function() { return val; }; } 10 | 11 | var sentinel = {}; 12 | var other = {}; 13 | 14 | var fakePromise = new FakePromise(); 15 | 16 | // Untrusted, non-Promises/A-compliant promise 17 | function FakePromise(val) { 18 | this.then = function (cb) { 19 | if (cb) { 20 | cb(val); 21 | } 22 | return this; 23 | }; 24 | } 25 | 26 | buster.testCase('when', { 27 | 'should return a promise for a value': function() { 28 | var result = when(1); 29 | assert(typeof result.then == 'function'); 30 | }, 31 | 32 | 'should return a promise for a promise': function() { 33 | var result = when(fakePromise); 34 | assert(typeof result.then == 'function'); 35 | }, 36 | 37 | 'should not return the input promise': function() { 38 | var result = when(fakePromise, identity); 39 | assert(typeof result.then == 'function'); 40 | refute.same(result, fakePromise); 41 | }, 42 | 43 | 'should return a promise that forwards for a value': function() { 44 | var result = when(1, constant(2)); 45 | 46 | assert(typeof result.then == 'function'); 47 | 48 | return result.then( 49 | function(val) { 50 | assert.equals(val, 2); 51 | }, 52 | fail 53 | ); 54 | }, 55 | 56 | 'should invoke fulfilled handler asynchronously for value': function() { 57 | var val = other; 58 | 59 | try { 60 | return when({}, function() { 61 | assert.same(val, sentinel); 62 | }); 63 | } finally { 64 | val = sentinel; 65 | } 66 | }, 67 | 68 | 'should invoke fulfilled handler asynchronously for fake promise': function() { 69 | var val = other; 70 | 71 | try { 72 | return when(fakePromise, function() { 73 | assert.same(val, sentinel); 74 | }); 75 | } finally { 76 | val = sentinel; 77 | } 78 | }, 79 | 80 | 'should invoke fulfilled handler asynchronously for resolved promise': function() { 81 | var val = other; 82 | 83 | try { 84 | return when(when.resolve(), function() { 85 | assert.same(val, sentinel); 86 | }); 87 | } finally { 88 | val = sentinel; 89 | } 90 | }, 91 | 92 | 'should invoke rejected handler asynchronously for rejected promise': function() { 93 | var val = other; 94 | 95 | try { 96 | return when(when.reject(), 97 | fail, function() { assert.same(val, sentinel); } 98 | ); 99 | } finally { 100 | val = sentinel; 101 | } 102 | }, 103 | 104 | 'should support deep nesting in promise chains': function() { 105 | var d, result; 106 | 107 | d = when.defer(); 108 | d.resolve(false); 109 | 110 | result = when(when(d.promise.then(function(val) { 111 | var d = when.defer(); 112 | d.resolve(val); 113 | return when(d.promise.then(identity), identity).then( 114 | function(val) { 115 | return !val; 116 | } 117 | ); 118 | }))); 119 | 120 | return result.then( 121 | function(val) { 122 | assert(val); 123 | }, 124 | fail 125 | ); 126 | }, 127 | 128 | 'should return a resolved promise for a resolved input promise': function() { 129 | return when(when.resolve(true)).then( 130 | function(val) { 131 | assert(val); 132 | }, 133 | fail 134 | ); 135 | }, 136 | 137 | 'should assimilate untrusted promises':function () { 138 | var untrusted, result; 139 | 140 | // unstrusted promise should never be returned by when() 141 | untrusted = new FakePromise(); 142 | result = when(untrusted); 143 | 144 | refute.equals(result, untrusted); 145 | refute(result instanceof FakePromise); 146 | }, 147 | 148 | 'should assimilate intermediate promises returned by callbacks':function () { 149 | var result; 150 | 151 | // untrusted promise returned by an intermediate 152 | // handler should be assimilated 153 | result = when(1, 154 | function (val) { 155 | return new FakePromise(val + 1); 156 | } 157 | ).then( 158 | function (val) { 159 | assert.equals(val, 2); 160 | }, 161 | fail 162 | ); 163 | 164 | refute(result instanceof FakePromise); 165 | 166 | return result; 167 | }, 168 | 169 | 'should assimilate intermediate promises and forward results':function () { 170 | var untrusted, result; 171 | 172 | untrusted = new FakePromise(1); 173 | 174 | result = when(untrusted, function (val) { 175 | return new FakePromise(val + 1); 176 | }); 177 | 178 | refute.equals(result, untrusted); 179 | refute(result instanceof FakePromise); 180 | 181 | return when(result, 182 | function (val) { 183 | assert.equals(val, 2); 184 | return new FakePromise(val + 1); 185 | } 186 | ).then( 187 | function (val) { 188 | assert.equals(val, 3); 189 | }, 190 | fail 191 | ); 192 | } 193 | }); 194 | -------------------------------------------------------------------------------- /test/with-test.js: -------------------------------------------------------------------------------- 1 | var buster = typeof window !== 'undefined' ? window.buster : require('buster'); 2 | var assert = buster.assert; 3 | 4 | var CorePromise = require('../lib/Promise'); 5 | var sentinel = { value: 'sentinel' }; 6 | 7 | buster.testCase('promise.with', { 8 | 'should set thisArg': function() { 9 | return CorePromise.resolve()['with'](sentinel).then(function() { 10 | assert.same(this, sentinel); 11 | }); 12 | }, 13 | 14 | 'should set thisArg when rejected': function() { 15 | return CorePromise.reject()['with'](sentinel).then(void 0, function() { 16 | assert.same(this, sentinel); 17 | }); 18 | }, 19 | 20 | 'should set thisArg for derived promises': function() { 21 | return CorePromise.resolve()['with'](sentinel).then(function(x) { 22 | return x; 23 | }).then(function() { 24 | assert.same(this, sentinel); 25 | }); 26 | }, 27 | 28 | 'should set thisArg for derived promises when rejected': function() { 29 | return CorePromise.resolve()['with'](sentinel).then(function(x) { 30 | throw x; 31 | }).then(void 0, function() { 32 | assert.same(this, sentinel); 33 | }); 34 | }, 35 | 36 | 'when called with no args': { 37 | 'should set default thisArg': function() { 38 | var expected; 39 | return CorePromise.resolve().then(function() { 40 | expected = this; 41 | }) 42 | ['with'](sentinel).then(function() { 43 | assert.same(this, sentinel); 44 | }) 45 | ['with']().then(function() { 46 | assert.same(this, expected); 47 | }); 48 | }, 49 | 50 | 'should set default thisArg when rejected': function() { 51 | var expected; 52 | return CorePromise.resolve().then(function() { 53 | expected = this; 54 | }) 55 | ['with'](sentinel).then(function() { 56 | assert.same(this, sentinel); 57 | throw sentinel; 58 | }) 59 | ['with']().then(void 0, function() { 60 | assert.same(this, expected); 61 | }); 62 | }, 63 | 64 | 'should set default thisArg for derived promises': function() { 65 | var expected; 66 | return CorePromise.resolve().then(function() { 67 | expected = this; 68 | }) 69 | ['with'](sentinel).then(function() { 70 | assert.same(this, sentinel); 71 | }) 72 | ['with']().then(function(x) { 73 | return x; 74 | }) 75 | .then(function() { 76 | assert.same(this, expected); 77 | }); 78 | }, 79 | 80 | 'should set default thisArg for derived promises when rejected': function() { 81 | var expected; 82 | return CorePromise.resolve().then(function() { 83 | expected = this; 84 | }) 85 | ['with'](sentinel).then(function() { 86 | assert.same(this, sentinel); 87 | }) 88 | ['with']().then(function(x) { 89 | throw x; 90 | }) 91 | .then(void 0, function() { 92 | assert.same(this, expected); 93 | }); 94 | } 95 | }, 96 | 97 | 'when called with non-object': { 98 | 'should mimic Function.prototype.call behavior': function() { 99 | var thisArg = 123; 100 | 101 | var expected = (function() { 102 | return this; 103 | }).call(thisArg); 104 | 105 | return CorePromise.resolve()['with'](thisArg).then(function() { 106 | assert.equals(this, expected); 107 | }); 108 | }, 109 | 110 | 'should mimic Function.prototype.call behavior when rejected': function() { 111 | var thisArg = 123; 112 | 113 | var expected = (function() { 114 | return this; 115 | }).call(thisArg); 116 | 117 | return CorePromise.reject()['with'](thisArg).then(void 0, function() { 118 | assert.equals(this, expected); 119 | }); 120 | } 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /timeout.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2011-2013 original author or authors */ 2 | 3 | /** 4 | * timeout.js 5 | * 6 | * Helper that returns a promise that rejects after a specified timeout, 7 | * if not explicitly resolved or rejected before that. 8 | * 9 | * @author Brian Cavalier 10 | * @author John Hann 11 | */ 12 | 13 | (function(define) { 14 | define(function(require) { 15 | 16 | var when = require('./when'); 17 | 18 | /** 19 | * @deprecated Use when(trigger).timeout(ms) 20 | */ 21 | return function timeout(msec, trigger) { 22 | return when(trigger).timeout(msec); 23 | }; 24 | }); 25 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 26 | 27 | 28 | -------------------------------------------------------------------------------- /unfold.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright B Cavalier & J Hann */ 2 | 3 | /** 4 | * unfold 5 | * @author: brian@hovercraftstudios.com 6 | */ 7 | (function(define) { 8 | define(function(require) { 9 | 10 | /** 11 | * @deprecated Use when.unfold 12 | */ 13 | return require('./when').unfold; 14 | 15 | }); 16 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); } ); 17 | 18 | -------------------------------------------------------------------------------- /unfold/list.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright B Cavalier & J Hann */ 2 | 3 | (function(define) { 4 | define(function(require) { 5 | 6 | var unfold = require('../when').unfold; 7 | 8 | /** 9 | * @deprecated 10 | * Given a seed and generator, produces an Array. Effectively the 11 | * dual (opposite) of when.reduce() 12 | * @param {function} generator function that generates a value (or promise 13 | * for a value) to be placed in the resulting array 14 | * @param {function} condition given a seed, must return truthy if the unfold 15 | * should continue, or falsey if it should terminate 16 | * @param {*|Promise} seed any value or promise 17 | * @return {Promise} resulting array 18 | */ 19 | return function list(generator, condition, seed) { 20 | var result = []; 21 | 22 | return unfold(generator, condition, append, seed)['yield'](result); 23 | 24 | function append(value, newSeed) { 25 | result.push(value); 26 | return newSeed; 27 | } 28 | }; 29 | 30 | }); 31 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 32 | 33 | --------------------------------------------------------------------------------