├── .editorconfig ├── .gitignore ├── .npmignore ├── .jshintignore ├── monitor ├── README.md ├── console.js ├── error.js ├── ConsoleReporter.js └── PromiseMonitor.js ├── test ├── browser │ └── index.html ├── monitor │ ├── lift-node.js │ ├── delay-handled.js │ ├── index.html │ ├── PromiseMonitor-test.js │ ├── all.js │ ├── map.js │ ├── done.js │ ├── unhandled-begets-unhandled.js │ ├── developer-error.js │ ├── unhandled-forever.js │ ├── multiple-escapes.js │ ├── unhandledRejectionApi-test.js │ ├── unhandled-handled-later.js │ └── deep-chain.js ├── buster.js ├── promises-aplus-adapter.js ├── unhandledRejection-test.js ├── else-test.js ├── browsers.json ├── unfold │ └── list-test.js ├── reject-test.js ├── format-test.js ├── isPromiseLike-test.js ├── join-test.js ├── parallel-test.js ├── fold-test.js ├── cycle-test.js ├── pipeline-test.js ├── delay-test.js ├── sequence-test.js ├── any-test.js ├── cancelable-test.js ├── settle-test.js ├── inspect-test.js ├── race-test.js ├── filter-test.js ├── liftAll-test.js ├── timeout-test.js ├── some-test.js ├── globalRejectionEvents-test.js ├── poll-test.js ├── with-test.js ├── all-test.js ├── map-test.js ├── guard-test.js ├── when-test.js ├── resolve-test.js ├── iterate-test.js ├── keys-test.js ├── flow-test.js └── sauce.js ├── es6-shim ├── README.md └── Promise.browserify-es6.js ├── node └── function.js ├── unfold.js ├── .travis.yml ├── lib ├── Promise.js ├── decorators │ ├── inspect.js │ ├── progress.js │ ├── fold.js │ ├── with.js │ ├── timed.js │ ├── unhandledRejection.js │ ├── iterate.js │ └── flow.js ├── TimeoutError.js ├── liftAll.js ├── state.js ├── apply.js ├── format.js ├── Scheduler.js └── env.js ├── .jshintrc ├── benchmark ├── index.html ├── map.js ├── run.js └── promise.js ├── monitor.js ├── delay.js ├── bower.json ├── timeout.js ├── unfold └── list.js ├── parallel.js ├── LICENSE.txt ├── sequence.js ├── CONTRIBUTING.md ├── pipeline.js ├── cancelable.js ├── guard.js ├── generator.js ├── keys.js ├── poll.js ├── docs ├── installation.md ├── es6-promise-shim.md └── debug-api.md ├── package.json ├── README.md └── function.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 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | benchmark/ 3 | experiments/ 4 | docs/ 5 | build/ 6 | *.md 7 | .* 8 | *~ 9 | bower.json 10 | bower_components/ -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | when.js browser tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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(); -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /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 | testbed: 'test/browser/index.html' 16 | }; 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /test/monitor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/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 | 8 | var makePromise = require('./makePromise'); 9 | var Scheduler = require('./Scheduler'); 10 | var async = require('./env').asap; 11 | 12 | return makePromise({ 13 | scheduler: new Scheduler(async) 14 | }); 15 | 16 | }); 17 | })(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /lib/decorators/inspect.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 inspect = require('../state').inspect; 9 | 10 | return function inspection(Promise) { 11 | 12 | Promise.prototype.inspect = function() { 13 | return inspect(Promise._handler(this)); 14 | }; 15 | 16 | return Promise; 17 | }; 18 | 19 | }); 20 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 21 | -------------------------------------------------------------------------------- /benchmark/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/decorators/progress.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 progress(Promise) { 9 | 10 | /** 11 | * @deprecated 12 | * Register a progress handler for this promise 13 | * @param {function} onProgress 14 | * @returns {Promise} 15 | */ 16 | Promise.prototype.progress = function(onProgress) { 17 | return this.then(void 0, void 0, onProgress); 18 | }; 19 | 20 | return Promise; 21 | }; 22 | 23 | }); 24 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/decorators/fold.js: -------------------------------------------------------------------------------- 1 | /** @license MIT License (c) copyright 2010-2014 original author or authors */ 2 | /** @author Brian Cavalier */ 3 | /** @author John Hann */ 4 | /** @author Jeff Escalante */ 5 | 6 | (function(define) { 'use strict'; 7 | define(function() { 8 | 9 | return function fold(Promise) { 10 | 11 | Promise.prototype.fold = function(f, z) { 12 | var promise = this._beget(); 13 | 14 | this._handler.fold(function(z, x, to) { 15 | Promise._handler(z).fold(function(x, z, to) { 16 | to.resolve(f.call(this, z, x)); 17 | }, x, this, to); 18 | }, z, promise._handler.receiver, promise._handler); 19 | 20 | return promise; 21 | }; 22 | 23 | return Promise; 24 | }; 25 | 26 | }); 27 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 28 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /lib/TimeoutError.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 | /** 9 | * Custom error type for promises rejected by promise.timeout 10 | * @param {string} message 11 | * @constructor 12 | */ 13 | function TimeoutError (message) { 14 | Error.call(this); 15 | this.message = message; 16 | this.name = TimeoutError.name; 17 | if (typeof Error.captureStackTrace === 'function') { 18 | Error.captureStackTrace(this, TimeoutError); 19 | } 20 | } 21 | 22 | TimeoutError.prototype = Object.create(Error.prototype); 23 | TimeoutError.prototype.constructor = TimeoutError; 24 | 25 | return TimeoutError; 26 | }); 27 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/apply.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 | makeApply.tryCatchResolve = tryCatchResolve; 9 | 10 | return makeApply; 11 | 12 | function makeApply(Promise, call) { 13 | if(arguments.length < 2) { 14 | call = tryCatchResolve; 15 | } 16 | 17 | return apply; 18 | 19 | function apply(f, thisArg, args) { 20 | var p = Promise._defer(); 21 | var l = args.length; 22 | var params = new Array(l); 23 | callAndResolve({ f:f, thisArg:thisArg, args:args, params:params, i:l-1, call:call }, p._handler); 24 | 25 | return p; 26 | } 27 | 28 | function callAndResolve(c, h) { 29 | if(c.i < 0) { 30 | return call(c.f, c.thisArg, c.params, h); 31 | } 32 | 33 | var handler = Promise._handler(c.args[c.i]); 34 | handler.fold(callAndResolveNext, c, void 0, h); 35 | } 36 | 37 | function callAndResolveNext(c, x, h) { 38 | c.params[c.i] = x; 39 | c.i -= 1; 40 | callAndResolve(c, h); 41 | } 42 | } 43 | 44 | function tryCatchResolve(f, thisArg, args, resolver) { 45 | try { 46 | resolver.resolve(f.apply(thisArg, args)); 47 | } catch(e) { 48 | resolver.reject(e); 49 | } 50 | } 51 | 52 | }); 53 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 54 | 55 | 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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 start` - starts buster server & prints a url 33 | 3. Point browsers at /capture, e.g. `localhost:1111/capture` 34 | 4. `npm run test-browser` 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /lib/Scheduler.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 | // Credit to Twisol (https://github.com/Twisol) for suggesting 9 | // this type of extensible queue + trampoline approach for next-tick conflation. 10 | 11 | /** 12 | * Async task scheduler 13 | * @param {function} async function to schedule a single async function 14 | * @constructor 15 | */ 16 | function Scheduler(async) { 17 | this._async = async; 18 | this._running = false; 19 | 20 | this._queue = this; 21 | this._queueLen = 0; 22 | this._afterQueue = {}; 23 | this._afterQueueLen = 0; 24 | 25 | var self = this; 26 | this.drain = function() { 27 | self._drain(); 28 | }; 29 | } 30 | 31 | /** 32 | * Enqueue a task 33 | * @param {{ run:function }} task 34 | */ 35 | Scheduler.prototype.enqueue = function(task) { 36 | this._queue[this._queueLen++] = task; 37 | this.run(); 38 | }; 39 | 40 | /** 41 | * Enqueue a task to run after the main task queue 42 | * @param {{ run:function }} task 43 | */ 44 | Scheduler.prototype.afterQueue = function(task) { 45 | this._afterQueue[this._afterQueueLen++] = task; 46 | this.run(); 47 | }; 48 | 49 | Scheduler.prototype.run = function() { 50 | if (!this._running) { 51 | this._running = true; 52 | this._async(this.drain); 53 | } 54 | }; 55 | 56 | /** 57 | * Drain the handler queue entirely, and then the after queue 58 | */ 59 | Scheduler.prototype._drain = function() { 60 | var i = 0; 61 | for (; i < this._queueLen; ++i) { 62 | this._queue[i].run(); 63 | this._queue[i] = void 0; 64 | } 65 | 66 | this._queueLen = 0; 67 | this._running = false; 68 | 69 | for (i = 0; i < this._afterQueueLen; ++i) { 70 | this._afterQueue[i].run(); 71 | this._afterQueue[i] = void 0; 72 | } 73 | 74 | this._afterQueueLen = 0; 75 | }; 76 | 77 | return Scheduler; 78 | 79 | }); 80 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 81 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/decorators/timed.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 env = require('../env'); 9 | var TimeoutError = require('../TimeoutError'); 10 | 11 | function setTimeout(f, ms, x, y) { 12 | return env.setTimer(function() { 13 | f(x, y, ms); 14 | }, ms); 15 | } 16 | 17 | return function timed(Promise) { 18 | /** 19 | * Return a new promise whose fulfillment value is revealed only 20 | * after ms milliseconds 21 | * @param {number} ms milliseconds 22 | * @returns {Promise} 23 | */ 24 | Promise.prototype.delay = function(ms) { 25 | var p = this._beget(); 26 | this._handler.fold(handleDelay, ms, void 0, p._handler); 27 | return p; 28 | }; 29 | 30 | function handleDelay(ms, x, h) { 31 | setTimeout(resolveDelay, ms, x, h); 32 | } 33 | 34 | function resolveDelay(x, h) { 35 | h.resolve(x); 36 | } 37 | 38 | /** 39 | * Return a new promise that rejects after ms milliseconds unless 40 | * this promise fulfills earlier, in which case the returned promise 41 | * fulfills with the same value. 42 | * @param {number} ms milliseconds 43 | * @param {Error|*=} reason optional rejection reason to use, defaults 44 | * to a TimeoutError if not provided 45 | * @returns {Promise} 46 | */ 47 | Promise.prototype.timeout = function(ms, reason) { 48 | var p = this._beget(); 49 | var h = p._handler; 50 | 51 | var t = setTimeout(onTimeout, ms, reason, p._handler); 52 | 53 | this._handler.visit(h, 54 | function onFulfill(x) { 55 | env.clearTimer(t); 56 | this.resolve(x); // this = h 57 | }, 58 | function onReject(x) { 59 | env.clearTimer(t); 60 | this.reject(x); // this = h 61 | }, 62 | h.notify); 63 | 64 | return p; 65 | }; 66 | 67 | function onTimeout(reason, h, ms) { 68 | var e = typeof reason === 'undefined' 69 | ? new TimeoutError('timed out after ' + ms + 'ms') 70 | : reason; 71 | h.reject(e); 72 | } 73 | 74 | return Promise; 75 | }; 76 | 77 | }); 78 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(require); })); 79 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/decorators/unhandledRejection.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 setTimer = require('../env').setTimer; 9 | var format = require('../format'); 10 | 11 | return function unhandledRejection(Promise) { 12 | 13 | var logError = noop; 14 | var logInfo = noop; 15 | var localConsole; 16 | 17 | if(typeof console !== 'undefined') { 18 | // Alias console to prevent things like uglify's drop_console option from 19 | // removing console.log/error. Unhandled rejections fall into the same 20 | // category as uncaught exceptions, and build tools shouldn't silence them. 21 | localConsole = console; 22 | logError = typeof localConsole.error !== 'undefined' 23 | ? function (e) { localConsole.error(e); } 24 | : function (e) { localConsole.log(e); }; 25 | 26 | logInfo = typeof localConsole.info !== 'undefined' 27 | ? function (e) { localConsole.info(e); } 28 | : function (e) { localConsole.log(e); }; 29 | } 30 | 31 | Promise.onPotentiallyUnhandledRejection = function(rejection) { 32 | enqueue(report, rejection); 33 | }; 34 | 35 | Promise.onPotentiallyUnhandledRejectionHandled = function(rejection) { 36 | enqueue(unreport, rejection); 37 | }; 38 | 39 | Promise.onFatalRejection = function(rejection) { 40 | enqueue(throwit, rejection.value); 41 | }; 42 | 43 | var tasks = []; 44 | var reported = []; 45 | var running = null; 46 | 47 | function report(r) { 48 | if(!r.handled) { 49 | reported.push(r); 50 | logError('Potentially unhandled rejection [' + r.id + '] ' + format.formatError(r.value)); 51 | } 52 | } 53 | 54 | function unreport(r) { 55 | var i = reported.indexOf(r); 56 | if(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/iterate.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 generate(Promise) { 9 | 10 | var resolve = Promise.resolve; 11 | 12 | Promise.iterate = iterate; 13 | Promise.unfold = unfold; 14 | 15 | return Promise; 16 | 17 | /** 18 | * @deprecated Use github.com/cujojs/most streams and most.iterate 19 | * Generate a (potentially infinite) stream of promised values: 20 | * x, f(x), f(f(x)), etc. until condition(x) returns true 21 | * @param {function} f function to generate a new x from the previous x 22 | * @param {function} condition function that, given the current x, returns 23 | * truthy when the iterate should stop 24 | * @param {function} handler function to handle the value produced by f 25 | * @param {*|Promise} x starting value, may be a promise 26 | * @return {Promise} the result of the last call to f before 27 | * condition returns true 28 | */ 29 | function iterate(f, condition, handler, x) { 30 | return unfold(function(x) { 31 | return [x, f(x)]; 32 | }, condition, handler, x); 33 | } 34 | 35 | /** 36 | * @deprecated Use github.com/cujojs/most streams and most.unfold 37 | * Generate a (potentially infinite) stream of promised values 38 | * by applying handler(generator(seed)) iteratively until 39 | * condition(seed) returns true. 40 | * @param {function} unspool function that generates a [value, newSeed] 41 | * given a seed. 42 | * @param {function} condition function that, given the current seed, returns 43 | * truthy when the unfold should stop 44 | * @param {function} handler function to handle the value produced by unspool 45 | * @param x {*|Promise} starting value, may be a promise 46 | * @return {Promise} the result of the last value produced by unspool before 47 | * condition returns true 48 | */ 49 | function unfold(unspool, condition, handler, x) { 50 | return resolve(x).then(function(seed) { 51 | return resolve(condition(seed)).then(function(done) { 52 | return done ? seed : resolve(unspool(seed)).spread(next); 53 | }); 54 | }); 55 | 56 | function next(item, newSeed) { 57 | return resolve(handler(item)).then(function() { 58 | return unfold(unspool, condition, handler, newSeed); 59 | }); 60 | } 61 | } 62 | }; 63 | 64 | }); 65 | }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); })); 66 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 process === '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 | return; 24 | } 25 | 26 | function listener(e) { 27 | buster.assert.same(e, sentinel); 28 | done(); 29 | } 30 | 31 | process.on('unhandledRejection', listener); 32 | 33 | CorePromise.reject(sentinel); 34 | }, 35 | 36 | 'should emit rejectionHandled': function(done) { 37 | if(typeof window !== 'undefined') { 38 | buster.assert(true); 39 | return; 40 | } 41 | 42 | var r; 43 | function unhandled(e, rejection) { 44 | buster.assert.same(e, sentinel); 45 | r = rejection; 46 | } 47 | 48 | function handled(rejection) { 49 | buster.assert.same(rejection, r); 50 | done(); 51 | } 52 | 53 | process.on('unhandledRejection', unhandled); 54 | process.on('rejectionHandled', handled); 55 | 56 | var p = CorePromise.reject(sentinel); 57 | setTimeout(function() { 58 | p.catch(function() {}); 59 | }, 10); 60 | } 61 | }, 62 | 63 | 'in Browser': { 64 | 'should emit unhandledRejection': function(done) { 65 | if(typeof window === 'undefined') { 66 | buster.assert(true); 67 | done(); 68 | return; 69 | } 70 | 71 | function listener(e) { 72 | window.removeEventListener('unhandledRejection', listener, false); 73 | e.preventDefault(); 74 | buster.assert.same(e.detail.reason, sentinel); 75 | done(); 76 | } 77 | 78 | window.addEventListener('unhandledRejection', listener, false); 79 | 80 | CorePromise.reject(sentinel); 81 | }, 82 | 83 | 'should emit rejectionHandled': function(done) { 84 | if(typeof window === 'undefined') { 85 | buster.assert(true); 86 | done(); 87 | return; 88 | } 89 | 90 | var key; 91 | function unhandled(e) { 92 | window.removeEventListener('unhandledRejection', unhandled, false); 93 | buster.assert.same(e.detail.reason, sentinel); 94 | key = e.detail.key; 95 | } 96 | 97 | function handled(e) { 98 | window.removeEventListener('rejectionHandled', handled, false); 99 | buster.assert.same(e.detail.key, key); 100 | done(); 101 | } 102 | 103 | window.addEventListener('unhandledRejection', unhandled, false); 104 | window.addEventListener('rejectionHandled', handled, false); 105 | 106 | var p = CorePromise.reject(sentinel); 107 | setTimeout(function() { 108 | p.catch(function() {}); 109 | }, 10); 110 | } 111 | } 112 | 113 | }); 114 | 115 | -------------------------------------------------------------------------------- /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 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | [CJS](http://wiki.commonjs.org/wiki/CommonJS) and [AMD](http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) are the primary targets for `when`, but instructions for a variety of setups are provided below. 5 | 6 | As of version 3.0, when.js requires an ES5 environment. In older environments, use an ES5 shim such as [poly](https://github.com/cujojs/poly) or [es5-shim](https://github.com/es-shims/es5-shim) 7 | 8 | #### RaveJS 9 | 10 | If you're already using [RaveJS](https://github.com/RaveJS/rave), just install when.js and start coding: 11 | 12 | 1. `npm install --save when` or `bower install --save when` 13 | 1. `var when = require('when');` 14 | 15 | #### AMD 16 | 17 | 1. Get it. `bower install --save when`, or `git clone https://github.com/cujojs/when` 18 | 19 | 1. Configure your loader. Here is an example of how configuring the package might look: 20 | 21 | ```js 22 | // using requirejs 23 | requirejs.config({ 24 | packages: [ 25 | { name: 'when', location: '/path/to/when', main: 'when' } 26 | ] 27 | }); 28 | 29 | // using curl.js 30 | curl.config({ 31 | packages: { 32 | when: { location: '/path/to/when', main: 'when' } 33 | } 34 | }); 35 | ``` 36 | 37 | 1. Load when wherever you need it. For example, as part of a module: 38 | 39 | ``` 40 | define(function(require) { 41 | var when = require('when'); 42 | 43 | // your code... 44 | }); 45 | ``` 46 | 47 | #### Node 48 | 49 | 1. `npm install --save when` 50 | 1. `var when = require('when');` 51 | 52 | #### RingoJS 53 | 54 | 1. `ringo-admin install cujojs/when` 55 | 1. `var when = require('when');` 56 | 57 | #### Ender 58 | 59 | 1. `ender add when` 60 | 2. `var when = require('when');` 61 | 62 | #### Other CommonJS environments (eg vert.x 1.x and 2.x with CommonJS module support) 63 | 64 | 1. `git clone https://github.com/cujojs/when` 65 | 1. `var when = require('when');` or `var when = require('path/to/when');` 66 | 67 | #### Browser environments (via browserify) 68 | 69 | If you prefer not to use an AMD or CommonJS loader in your project, you can use a pre-built UMD module available in `dist/browser/when[.min|.debug].js` to have a global `when` available. 70 | 71 | 1. `npm install --save when` 72 | 1. `` 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "when", 3 | "version": "3.7.7", 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 | "jshint": "~2", 54 | "json5": "~0.2", 55 | "microtime": "~2", 56 | "optimist": "~0.6", 57 | "poly": "^0.6.1", 58 | "promises-aplus-tests": "~2", 59 | "rest": "1.1.x", 60 | "sauce-connect-launcher": "~0.4", 61 | "uglify-js": "~2", 62 | "wd": "~0.2" 63 | }, 64 | "main": "when.js", 65 | "ender": { 66 | "files": [ 67 | "*.js", 68 | "lib/*.js", 69 | "node/*.js", 70 | "unfold/*.js", 71 | "monitor/*.js", 72 | "lib/decorators/*.js" 73 | ] 74 | }, 75 | "browser": { 76 | "when": "./dist/browser/when.js", 77 | "vertx": false 78 | }, 79 | "directories": { 80 | "test": "test" 81 | }, 82 | "scripts": { 83 | "test": "jshint . && buster-test -e node && promises-aplus-tests test/promises-aplus-adapter.js", 84 | "build-browser-test": "browserify ./node_modules/poly/es5.js -o test/browser/es5.js && browserify ./test/*-test.js ./test/**/*-test.js -o test/browser/tests.js -x buster ", 85 | "browser-test": "npm run build-browser-test && buster-static -e browser -p 8080", 86 | "ci": "npm test && node test/sauce.js", 87 | "tunnel": "node test/sauce.js -m", 88 | "start": "buster-static -e browser", 89 | "benchmark": "node benchmark/promise && node benchmark/map", 90 | "prepublish": "npm run browserify && npm run uglify", 91 | "preversion": "npm run browserify && npm run uglify", 92 | "browserify": "npm run browserify-es6 && npm run browserify-when && npm run browserify-debug", 93 | "browserify-es6": "browserify -s Promise es6-shim/Promise.browserify-es6.js --no-detect-globals --debug | exorcist -b . -r https://raw.githubusercontent.com/cujojs/when/`git rev-parse HEAD` es6-shim/Promise.js.map >es6-shim/Promise.js", 94 | "browserify-when": "mkdir -p dist/browser && browserify -s when build/when.browserify.js --no-detect-globals --debug | exorcist -b . -r https://raw.githubusercontent.com/cujojs/when/`git rev-parse HEAD` dist/browser/when.js.map >dist/browser/when.js", 95 | "browserify-debug": "mkdir -p dist/browser && browserify -s when build/when.browserify-debug.js --no-detect-globals --debug | exorcist -b . -r https://raw.githubusercontent.com/cujojs/when/`git rev-parse HEAD` dist/browser/when.debug.js.map >dist/browser/when.debug.js", 96 | "uglify": "npm run uglify-es6 && npm run uglify-when", 97 | "uglify-es6": "cd es6-shim; uglifyjs Promise.js --compress --mangle --in-source-map Promise.js.map --source-map Promise.min.js.map -o Promise.min.js; cd ../..", 98 | "uglify-when": "cd dist/browser; uglifyjs when.js --compress --mangle --in-source-map when.js.map --source-map when.min.js.map -o when.min.js; cd ../.." 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /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 | // returns a when.js promise for 'hello world' 61 | return rest('http://example.com/greeting'); 62 | } 63 | 64 | function addExclamation(greeting) { 65 | return greeting + '!!!!' 66 | } 67 | 68 | function handleError(e) { 69 | return 'drat!'; 70 | } 71 | ``` 72 | 73 | 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. 74 | 75 | ```js 76 | var when = require('when'); 77 | var rest = require('rest'); 78 | 79 | when.reduce(when.map(getRemoteNumberList(), times10), sum) 80 | .done(function(result) { 81 | console.log(result); 82 | }); 83 | 84 | function getRemoteNumberList() { 85 | // Get a remote array [1, 2, 3, 4, 5] 86 | return rest('http://example.com/numbers').then(JSON.parse); 87 | } 88 | 89 | function sum(x, y) { return x + y; } 90 | function times10(x) {return x * 10; } 91 | ``` 92 | 93 | License 94 | ------- 95 | 96 | Licensed under MIT. [Full license here »](LICENSE.txt) 97 | 98 | Contributing 99 | ------------ 100 | 101 | Please see the [contributing guide](CONTRIBUTING.md) for more information on running tests, opening issues, and contributing code to the project. 102 | 103 | References 104 | ---------- 105 | 106 | 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). 107 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |