package/package.json000644 0000002266 3560116604 011554 0ustar00000000 000000 { "name": "vow", "version": "0.4.20", "description": "DOM Promise and Promises/A+ implementation for Node.js and browsers", "homepage": "http://dfilatov.github.io/vow/", "keywords": [ "nodejs", "browser", "async", "promise", "dom", "a+" ], "author": "Dmitry Filatov ", "contributors": [ { "name": "Dmitry Filatov", "email": "dfilatov@yandex-team.ru" } ], "repository": { "type": "git", "url": "http://github.com/dfilatov/vow.git" }, "dependencies": {}, "devDependencies": { "nodeunit": "0.11.3", "istanbul": "0.4.5", "terser": "4.0.2", "promises-aplus-tests": "2.1.0", "marked": "0.6.3", "jspath": "0.2.11", "yate": "0.0.65", "highlight.js": "7.5.0", "bem-jsd": "1.3.1" }, "license": "MIT", "main": "lib/vow", "engines": { "node": ">= 0.4.0" }, "scripts": { "test": "./node_modules/istanbul/lib/cli.js test test/utils/runner.js" }, "enb": { "sources": [ "lib/vow.js" ] } } package/CHANGELOG.md000644 0000011231 3560116604 011067 0ustar00000000 000000 Changelog ========= 0.4.18 ----- * Added `promise.finally` [#116] (https://github.com/dfilatov/vow/issues/116) 0.4.17 ----- * Fixed an issue with wrong resolving after adding `onProgress` callback 0.4.16 ----- * Fixed an issue with throwing exception within resolver function [#113] (https://github.com/dfilatov/vow/issues/114) 0.4.15 ----- * Fixed an issue with double emit of `PromiseRejectionEvent` [#113] (https://github.com/dfilatov/vow/issues/113) 0.4.14 ----- * Added support of unhandled rejection event [#111] (https://github.com/dfilatov/vow/issues/111) 0.4.13 ----- * Added workaround to avoid bug with `Array.prototype.push` in Opera 41 0.4.12 ----- * Wrong propagation of progress state fixed 0.4.11 ----- * Now global object (`window`, `global`) is passed properly 0.4.10 ----- * Now `MutationObserver` is used for internal "next tick" operations 0.4.9 ----- * `vow.cast` method was fixed to properly work with external promises [#88](https://github.com/dfilatov/vow/issues/88) 0.4.8 ----- * Detection of ymaps modular system was improved [#82](https://github.com/dfilatov/vow/issues/82) 0.4.7 ----- * `vow.all` had wrong behaviour in case of passing of another promise implementation [#77](https://github.com/dfilatov/vow/issues/77) * `vow.timeout` rejects with `vow.TimedOutError` instead of `Error` reason in case of timeout [#76](https://github.com/dfilatov/vow/issues/76) 0.4.6 ----- * `defer.reject` had wrong behaviour in case of already rejected promise was passed [#72](https://github.com/dfilatov/vow/issues/72) * CommonJS environment detection became more accurate [#74](https://github.com/dfilatov/vow/issues/74) 0.4.5 ----- * Throwing exceptions inside `vow.reject` was removed [#69](https://github.com/dfilatov/vow/issues/69) * `promise.isFulfilled`/`promise.isRejected` immediately return proper state of promise got from `vow.fulfill(value)`/`reject(value`) [#68](https://github.com/dfilatov/vow/issues/68) * Minor optimizations were added 0.4.4 ----- * ENB sources were added 0.4.3 ----- * Some optimizations for V8 were added [#60](https://github.com/dfilatov/vow/issues/60). Thanks to [B-Vladi](https://github.com/B-Vladi). 0.4.2 ----- * Pass progress state from items in all arrays/objects methods [#58](https://github.com/dfilatov/vow/issues/58) 0.4.1 ----- * Improve detection of vow-compatible promises 0.4.0 ----- * Implement [DOM Promise](http://dom.spec.whatwg.org/#promises) specification * Implement [new Promise A+](https://github.com/promises-aplus/promises-spec) specification * Remove `promise.fulfill`, `promise.reject`, `promise.notify` methods * Add `vow.anyResolved` method [#53](https://github.com/dfilatov/vow/issues/53) * Add `vow.cast` method [#53](https://github.com/dfilatov/vow/issues/56) 0.3.12 ------ * Make `Promise` class accessible from outside 0.3.11 ------ * Fix bug with inner timer in `delay` method [#45](https://github.com/dfilatov/jspromise/issues/45) 0.3.10 ------ * Use `setImmediate` instead of `process.nextTick` in Node.js >= 0.10.x [#40](https://github.com/dfilatov/jspromise/issues/40) * Up Promises/A+ Compliance Test Suite to 1.3.2 0.3.9 ----- * Fix for propagation of progress state [#37](https://github.com/dfilatov/jspromise/issues/37) 0.3.8 ----- * Fix for ignoring callback's context in always method [#35](https://github.com/dfilatov/jspromise/issues/35) * Callback in `Vow.invoke` called in global context now * bower.json added [#34](https://github.com/dfilatov/jspromise/issues/34) 0.3.7 ----- * `Vow.allPatiently` method added [#32](https://github.com/dfilatov/jspromise/issues/32) 0.3.6 ----- * Fix for properly work in mocha/phantomjs environment [#31](https://github.com/dfilatov/jspromise/issues/31) 0.3.5 ----- * Fix for synchronize `onProgress` callback in `promise.sync` method [#30](https://github.com/dfilatov/jspromise/issues/30) 0.3.4 ----- * Add ability to use multiple modules system simultaneously [#26](https://github.com/dfilatov/jspromise/issues/26) * Add callbacks to `promise.done` method [#29](https://github.com/dfilatov/jspromise/issues/29) 0.3.3 ----- * Use `Vow` instead `this` in all static methods * Speed up optimizations 0.3.2 ----- * Ability to specify context for callbacks [#28](https://github.com/dfilatov/jspromise/issues/28) 0.3.1 ----- * Add support for [ym module's system](https://github.com/ymaps/modules) [#24](https://github.com/dfilatov/jspromise/issues/24) 0.3.0 ----- * Add support for `progress/notify` [#23](https://github.com/dfilatov/jspromise/issues/23) 0.2.6 ----- * `promise.always` doesn't pass the return value of `onResolved` [#19](https://github.com/dfilatov/jspromise/issues/19) package/LICENSE000644 0000002071 3560116604 010265 0ustar00000000 000000 The MIT License (MIT) Copyright (c) 2012 Dmitry Filatov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package/README.md000644 0000005744 3560116604 010551 0ustar00000000 000000 Vow [![NPM Version](https://img.shields.io/npm/v/vow.svg?style=flat-square)](https://www.npmjs.com/package/vow) [![Build Status](https://img.shields.io/travis/dfilatov/vow/master.svg?style=flat-square)](https://travis-ci.org/dfilatov/vow/branches) [![NPM Downloads](https://img.shields.io/npm/dm/vow.svg?style=flat-square)](https://www.npmjs.org/package/vow) ========= Vow is a [Promises/A+](http://promisesaplus.com/) implementation. It also supports [ES6 Promises](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects) specification. Full API reference can be found at http://dfilatov.github.io/vow/. Getting Started --------------- ### In Node.js ### You can install using Node Package Manager (npm): npm install vow ### In Browsers ### ```html ``` It also supports RequireJS module format and [YM module](https://github.com/ymaps/modules) format. Vow has been tested in IE6+, Mozilla Firefox 3+, Chrome 5+, Safari 5+, Opera 10+. Usage ----- ### Creating a promise ### There are two possible ways to create a promise. #### 1. Using a deferred #### ```js function doSomethingAsync() { var deferred = vow.defer(); // now you can resolve, reject, notify corresponging promise within `deferred` // e.g. `defer.resolve('ok');` return deferred.promise(); // and return corresponding promise to subscribe to reactions } doSomethingAsync().then( function() {}, // onFulfilled reaction function() {}, // onRejected reaction function() {} // onNotified reaction ); ``` The difference between `deferred` and `promise` is that `deferred` contains methods to resolve, reject and notify corresponding promise, but the `promise` by itself allows only to subscribe on these actions. #### 2. ES6-compatible way #### ```js function doSomethingAsync() { return new vow.Promise(function(resolve, reject, notify) { // now you can resolve, reject, notify the promise }); } doSomethingAsync().then( function() {}, // onFulfilled reaction function() {}, // onRejected reaction function() {} // onNotified reaction ); ``` Extensions and related projects ------------------------------- * [vow-fs](https://github.com/dfilatov/vow-fs) — vow-based file I/O for Node.js * [vow-node](https://github.com/dfilatov/vow-node) — extension for vow to work with nodejs-style callbacks * [vow-queue](https://github.com/dfilatov/vow-queue) — vow-based task queue with weights and priorities * [vow-asker](https://github.com/nodules/vow-asker) — wraps [asker](https://github.com/nodules/asker) API in the vow promises implementation **NOTE**. Documentation for old versions of the library can be found at https://github.com/dfilatov/vow/blob/0.3.x/README.md. package/lib/vow.js000644 0000123652 3560116604 011210 0ustar00000000 000000 /** * @module vow * @author Filatov Dmitry * @version 0.4.20 * @license * Dual licensed under the MIT and GPL licenses: * * http://www.opensource.org/licenses/mit-license.php * * http://www.gnu.org/licenses/gpl.html */ (function(global) { var undef, nextTick = (function() { var fns = [], enqueueFn = function(fn) { fns.push(fn); return fns.length === 1; }, callFns = function() { var fnsToCall = fns, i = 0, len = fns.length; fns = []; while(i < len) { fnsToCall[i++](); } }; var MutationObserver = global.MutationObserver || global.WebKitMutationObserver; // modern browsers if(MutationObserver) { var num = 1, node = document.createTextNode(''); new MutationObserver(callFns).observe(node, { characterData : true }); return function(fn) { enqueueFn(fn) && (node.data = (num *= -1)); }; } if(typeof process === 'object' && process.nextTick) { return function(fn) { enqueueFn(fn) && process.nextTick(callFns); }; } if(typeof setImmediate === 'function') { return function(fn) { enqueueFn(fn) && setImmediate(callFns); }; } if(global.postMessage) { var isPostMessageAsync = true; if(global.attachEvent) { var checkAsync = function() { isPostMessageAsync = false; }; global.attachEvent('onmessage', checkAsync); global.postMessage('__checkAsync', '*'); global.detachEvent('onmessage', checkAsync); } if(isPostMessageAsync) { var msg = '__promise' + Math.random() + '_' +new Date, onMessage = function(e) { if(e.data === msg) { e.stopPropagation && e.stopPropagation(); callFns(); } }; global.addEventListener? global.addEventListener('message', onMessage, true) : global.attachEvent('onmessage', onMessage); return function(fn) { enqueueFn(fn) && global.postMessage(msg, '*'); }; } } var doc = global.document; if('onreadystatechange' in doc.createElement('script')) { // ie6-ie8 var createScript = function() { var script = doc.createElement('script'); script.onreadystatechange = function() { script.parentNode.removeChild(script); script = script.onreadystatechange = null; callFns(); }; (doc.documentElement || doc.body).appendChild(script); }; return function(fn) { enqueueFn(fn) && createScript(); }; } return function(fn) { // old browsers enqueueFn(fn) && setTimeout(callFns, 0); }; })(), throwException = function(e) { nextTick(function() { throw e; }); }, isFunction = function(obj) { return typeof obj === 'function'; }, isObject = function(obj) { return obj !== null && typeof obj === 'object'; }, toStr = Object.prototype.toString, isArray = Array.isArray || function(obj) { return toStr.call(obj) === '[object Array]'; }, getArrayKeys = function(arr) { var res = [], i = 0, len = arr.length; while(i < len) { res.push(i++); } return res; }, getObjectKeys = Object.keys || function(obj) { var res = []; for(var i in obj) { obj.hasOwnProperty(i) && res.push(i); } return res; }, defineCustomErrorType = function(name) { var res = function(message) { this.name = name; this.message = message; }; res.prototype = new Error(); return res; }, wrapOnFulfilled = function(onFulfilled, idx) { return function(val) { onFulfilled.call(this, val, idx); }; }, emitUnhandledRejection = global.PromiseRejectionEvent? function(reason, promise) { new global.PromiseRejectionEvent( 'unhandledrejection', { promise : promise, reason : reason }); } : typeof process === 'object' && process.emit? function(reason, promise) { process.emit('unhandledRejection', reason, promise); } : function() {}; /** * @class Deferred * @exports vow:Deferred * @description * The `Deferred` class is used to encapsulate newly-created promise object along with functions that resolve, reject or notify it. */ /** * @constructor * @description * You can use `vow.defer()` instead of using this constructor. * * `new vow.Deferred()` gives the same result as `vow.defer()`. */ var Deferred = function() { this._promise = new Promise(); }; Deferred.prototype = /** @lends Deferred.prototype */{ /** * Returns the corresponding promise. * * @returns {vow:Promise} */ promise : function() { return this._promise; }, /** * Resolves the corresponding promise with the given `value`. * * @param {*} value * * @example * ```js * var defer = vow.defer(), * promise = defer.promise(); * * promise.then(function(value) { * // value is "'success'" here * }); * * defer.resolve('success'); * ``` */ resolve : function(value) { this._promise.isResolved() || this._promise._resolve(value); }, /** * Rejects the corresponding promise with the given `reason`. * * @param {*} reason * * @example * ```js * var defer = vow.defer(), * promise = defer.promise(); * * promise.fail(function(reason) { * // reason is "'something is wrong'" here * }); * * defer.reject('something is wrong'); * ``` */ reject : function(reason) { if(this._promise.isResolved()) { return; } if(vow.isPromise(reason)) { reason = reason.then(function(val) { var defer = vow.defer(); defer.reject(val); return defer.promise(); }); this._promise._resolve(reason); } else { this._promise._reject(reason); } }, /** * Notifies the corresponding promise with the given `value`. * * @param {*} value * * @example * ```js * var defer = vow.defer(), * promise = defer.promise(); * * promise.progress(function(value) { * // value is "'20%'", "'40%'" here * }); * * defer.notify('20%'); * defer.notify('40%'); * ``` */ notify : function(value) { this._promise.isResolved() || this._promise._notify(value); } }; var PROMISE_STATUS = { PENDING : 0, RESOLVED : 1, FULFILLED : 2, REJECTED : 3 }; /** * @class Promise * @exports vow:Promise * @description * The `Promise` class is used when you want to give to the caller something to subscribe to, * but not the ability to resolve or reject the deferred. */ /** * @constructor * @param {Function} resolver See https://github.com/domenic/promises-unwrapping/blob/master/README.md#the-promise-constructor for details. * @description * You should use this constructor directly only if you are going to use `vow` as DOM Promises implementation. * In other case you should use `vow.defer()` and `defer.promise()` methods. * @example * ```js * function fetchJSON(url) { * return new vow.Promise(function(resolve, reject, notify) { * var xhr = new XMLHttpRequest(); * xhr.open('GET', url); * xhr.responseType = 'json'; * xhr.send(); * xhr.onload = function() { * if(xhr.response) { * resolve(xhr.response); * } * else { * reject(new TypeError()); * } * }; * }); * } * ``` */ var Promise = function(resolver) { this._value = undef; this._status = PROMISE_STATUS.PENDING; this._shouldEmitUnhandledRejection = true; this._fulfilledCallbacks = []; this._rejectedCallbacks = []; this._progressCallbacks = []; if(resolver) { // NOTE: see https://github.com/domenic/promises-unwrapping/blob/master/README.md var _this = this, resolverFnLen = resolver.length; try { resolver( function(val) { _this.isResolved() || _this._resolve(val); }, resolverFnLen > 1? function(reason) { _this.isResolved() || _this._reject(reason); } : undef, resolverFnLen > 2? function(val) { _this.isResolved() || _this._notify(val); } : undef); } catch(e) { this._reject(e); } } }; Promise.prototype = /** @lends Promise.prototype */ { /** * Returns the value of the fulfilled promise or the reason in case of rejection. * * @returns {*} */ valueOf : function() { return this._value; }, /** * Returns `true` if the promise is resolved. * * @returns {Boolean} */ isResolved : function() { return this._status !== PROMISE_STATUS.PENDING; }, /** * Returns `true` if the promise is fulfilled. * * @returns {Boolean} */ isFulfilled : function() { return this._status === PROMISE_STATUS.FULFILLED; }, /** * Returns `true` if the promise is rejected. * * @returns {Boolean} */ isRejected : function() { return this._status === PROMISE_STATUS.REJECTED; }, /** * Adds reactions to the promise. * * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Function} [onProgress] Callback that will be invoked with a provided value after the promise has been notified * @param {Object} [ctx] Context of the callbacks execution * @returns {vow:Promise} A new promise, see https://github.com/promises-aplus/promises-spec for details */ then : function(onFulfilled, onRejected, onProgress, ctx) { this._shouldEmitUnhandledRejection = false; var defer = new Deferred(); this._addCallbacks(defer, onFulfilled, onRejected, onProgress, ctx); return defer.promise(); }, /** * Adds only a rejection reaction. This method is a shorthand for `promise.then(undefined, onRejected)`. * * @param {Function} onRejected Callback that will be called with a provided 'reason' as argument after the promise has been rejected * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ 'catch' : function(onRejected, ctx) { return this.then(undef, onRejected, ctx); }, /** * Adds only a rejection reaction. This method is a shorthand for `promise.then(null, onRejected)`. It's also an alias for `catch`. * * @param {Function} onRejected Callback to be called with the value after promise has been rejected * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ fail : function(onRejected, ctx) { return this.then(undef, onRejected, ctx); }, /** * Adds a resolving reaction (for both fulfillment and rejection). * * @param {Function} onResolved Callback that will be invoked with the promise as an argument, after the promise has been resolved. * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ always : function(onResolved, ctx) { var _this = this, cb = function() { return onResolved.call(this, _this); }; return this.then(cb, cb, ctx); }, /** * Adds a resolving reaction (for both fulfillment and rejection). The returned promise will be fullfiled with the same value or rejected with the same reason as the original promise. * * @param {Function} onFinalized Callback that will be invoked after the promise has been resolved. * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ 'finally' : function(onFinalized, ctx) { var _this = this, cb = function() { return onFinalized.call(this); }; return this .then(cb, cb, ctx) .then(function() { return _this; }); }, /** * Adds a progress reaction. * * @param {Function} onProgress Callback that will be called with a provided value when the promise has been notified * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ progress : function(onProgress, ctx) { return this.then(undef, undef, onProgress, ctx); }, /** * Like `promise.then`, but "spreads" the array into a variadic value handler. * It is useful with the `vow.all` and the `vow.allResolved` methods. * * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Object} [ctx] Context of the callbacks execution * @returns {vow:Promise} * * @example * ```js * var defer1 = vow.defer(), * defer2 = vow.defer(); * * vow.all([defer1.promise(), defer2.promise()]).spread(function(arg1, arg2) { * // arg1 is "1", arg2 is "'two'" here * }); * * defer1.resolve(1); * defer2.resolve('two'); * ``` */ spread : function(onFulfilled, onRejected, ctx) { return this.then( function(val) { return onFulfilled.apply(this, val); }, onRejected, ctx); }, /** * Like `then`, but terminates a chain of promises. * If the promise has been rejected, this method throws it's "reason" as an exception in a future turn of the event loop. * * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Function} [onProgress] Callback that will be invoked with a provided value after the promise has been notified * @param {Object} [ctx] Context of the callbacks execution * * @example * ```js * var defer = vow.defer(); * defer.reject(Error('Internal error')); * defer.promise().done(); // exception to be thrown * ``` */ done : function(onFulfilled, onRejected, onProgress, ctx) { this .then(onFulfilled, onRejected, onProgress, ctx) .fail(throwException); }, /** * Returns a new promise that will be fulfilled in `delay` milliseconds if the promise is fulfilled, * or immediately rejected if the promise is rejected. * * @param {Number} delay * @returns {vow:Promise} */ delay : function(delay) { var timer, promise = this.then(function(val) { var defer = new Deferred(); timer = setTimeout( function() { defer.resolve(val); }, delay); return defer.promise(); }); promise.always(function() { clearTimeout(timer); }); return promise; }, /** * Returns a new promise that will be rejected in `timeout` milliseconds * if the promise is not resolved beforehand. * * @param {Number} timeout * @returns {vow:Promise} * * @example * ```js * var defer = vow.defer(), * promiseWithTimeout1 = defer.promise().timeout(50), * promiseWithTimeout2 = defer.promise().timeout(200); * * setTimeout( * function() { * defer.resolve('ok'); * }, * 100); * * promiseWithTimeout1.fail(function(reason) { * // promiseWithTimeout to be rejected in 50ms * }); * * promiseWithTimeout2.then(function(value) { * // promiseWithTimeout to be fulfilled with "'ok'" value * }); * ``` */ timeout : function(timeout) { var defer = new Deferred(), timer = setTimeout( function() { defer.reject(new vow.TimedOutError('timed out')); }, timeout); this.then( function(val) { defer.resolve(val); }, function(reason) { defer.reject(reason); }); defer.promise().always(function() { clearTimeout(timer); }); return defer.promise(); }, _vow : true, _resolve : function(val) { if(this._status > PROMISE_STATUS.RESOLVED) { return; } if(val === this) { this._reject(TypeError('Can\'t resolve promise with itself')); return; } this._status = PROMISE_STATUS.RESOLVED; if(val && !!val._vow) { // shortpath for vow.Promise if(val.isFulfilled()) { this._fulfill(val.valueOf()); } else if(val.isRejected()) { val._shouldEmitUnhandledRejection = false; this._reject(val.valueOf()); } else { val.then( this._fulfill, this._reject, this._notify, this); } return; } if(isObject(val) || isFunction(val)) { var then; try { then = val.then; } catch(e) { this._reject(e); return; } if(isFunction(then)) { var _this = this, isResolved = false; try { then.call( val, function(val) { if(isResolved) { return; } isResolved = true; _this._resolve(val); }, function(err) { if(isResolved) { return; } isResolved = true; _this._reject(err); }, function(val) { _this._notify(val); }); } catch(e) { isResolved || this._reject(e); } return; } } this._fulfill(val); }, _fulfill : function(val) { if(this._status > PROMISE_STATUS.RESOLVED) { return; } this._status = PROMISE_STATUS.FULFILLED; this._value = val; this._callCallbacks(this._fulfilledCallbacks, val); this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = undef; }, _reject : function(reason) { if(this._status > PROMISE_STATUS.RESOLVED) { return; } this._status = PROMISE_STATUS.REJECTED; this._value = reason; this._callCallbacks(this._rejectedCallbacks, reason); if(!this._rejectedCallbacks.length) { var _this = this; nextTick(function() { if(_this._shouldEmitUnhandledRejection) { emitUnhandledRejection(reason, _this); } }); } this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = undef; }, _notify : function(val) { this._callCallbacks(this._progressCallbacks, val); }, _addCallbacks : function(defer, onFulfilled, onRejected, onProgress, ctx) { if(onRejected && !isFunction(onRejected)) { ctx = onRejected; onRejected = undef; } else if(onProgress && !isFunction(onProgress)) { ctx = onProgress; onProgress = undef; } if(onRejected) { this._shouldEmitUnhandledRejection = false; } var cb; if(!this.isRejected()) { cb = { defer : defer, fn : isFunction(onFulfilled)? onFulfilled : undef, ctx : ctx }; this.isFulfilled()? this._callCallbacks([cb], this._value) : this._fulfilledCallbacks.push(cb); } if(!this.isFulfilled()) { cb = { defer : defer, fn : onRejected, ctx : ctx }; this.isRejected()? this._callCallbacks([cb], this._value) : this._rejectedCallbacks.push(cb); } if(this._status <= PROMISE_STATUS.RESOLVED) { this._progressCallbacks.push({ defer : defer, fn : onProgress, ctx : ctx }); } }, _callCallbacks : function(callbacks, arg) { var len = callbacks.length; if(!len) { return; } var isResolved = this.isResolved(), isFulfilled = this.isFulfilled(), isRejected = this.isRejected(); nextTick(function() { var i = 0, cb, defer, fn; while(i < len) { cb = callbacks[i++]; defer = cb.defer; fn = cb.fn; if(fn) { var ctx = cb.ctx, res; try { res = ctx? fn.call(ctx, arg) : fn(arg); } catch(e) { defer.reject(e); continue; } isFulfilled || isRejected? defer.resolve(res) : defer.notify(res); } else if(isFulfilled) { defer.resolve(arg); } else if(isRejected) { defer.reject(arg); } else { defer.notify(arg); } } }); } }; /** @lends Promise */ var staticMethods = { /** * Coerces the given `value` to a promise, or returns the `value` if it's already a promise. * * @param {*} value * @returns {vow:Promise} */ cast : function(value) { return vow.cast(value); }, /** * Returns a promise, that will be fulfilled only after all the items in `iterable` are fulfilled. * If any of the `iterable` items gets rejected, then the returned promise will be rejected. * * @param {Array|Object} iterable * @returns {vow:Promise} */ all : function(iterable) { return vow.all(iterable); }, /** * Returns a promise, that will be fulfilled only after all the items in `iterable` are fulfilled or rejected. * * @param {Array|Object} iterable * @returns {vow:Promise} */ allSettled : function(iterable) { return vow.allSettled(iterable); }, /** * Returns a promise, that will be fulfilled only when any of the items in `iterable` are fulfilled. * If any of the `iterable` items gets rejected, then the returned promise will be rejected. * * @param {Array} iterable * @returns {vow:Promise} */ race : function(iterable) { return vow.anyResolved(iterable); }, /** * Returns a promise that has already been resolved with the given `value`. * If `value` is a promise, the returned promise will have `value`'s state. * * @param {*} value * @returns {vow:Promise} */ resolve : function(value) { return vow.resolve(value); }, /** * Returns a promise that has already been rejected with the given `reason`. * * @param {*} reason * @returns {vow:Promise} */ reject : function(reason) { return vow.reject(reason); } }; for(var prop in staticMethods) { staticMethods.hasOwnProperty(prop) && (Promise[prop] = staticMethods[prop]); } var vow = /** @exports vow */ { Deferred : Deferred, Promise : Promise, /** * Creates a new deferred. This method is a factory method for `vow:Deferred` class. * It's equivalent to `new vow.Deferred()`. * * @returns {vow:Deferred} */ defer : function() { return new Deferred(); }, /** * Static equivalent to `promise.then`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Function} [onProgress] Callback that will be invoked with a provided value after the promise has been notified * @param {Object} [ctx] Context of the callbacks execution * @returns {vow:Promise} */ when : function(value, onFulfilled, onRejected, onProgress, ctx) { return vow.cast(value).then(onFulfilled, onRejected, onProgress, ctx); }, /** * Static equivalent to `promise.fail`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} onRejected Callback that will be invoked with a provided reason after the promise has been rejected * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ fail : function(value, onRejected, ctx) { return vow.when(value, undef, onRejected, ctx); }, /** * Static equivalent to `promise.always`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} onResolved Callback that will be invoked with the promise as an argument, after the promise has been resolved. * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ always : function(value, onResolved, ctx) { return vow.when(value).always(onResolved, ctx); }, /** * Static equivalent to `promise.progress`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} onProgress Callback that will be invoked with a provided value after the promise has been notified * @param {Object} [ctx] Context of the callback execution * @returns {vow:Promise} */ progress : function(value, onProgress, ctx) { return vow.when(value).progress(onProgress, ctx); }, /** * Static equivalent to `promise.spread`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Object} [ctx] Context of the callbacks execution * @returns {vow:Promise} */ spread : function(value, onFulfilled, onRejected, ctx) { return vow.when(value).spread(onFulfilled, onRejected, ctx); }, /** * Static equivalent to `promise.done`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Function} [onFulfilled] Callback that will be invoked with a provided value after the promise has been fulfilled * @param {Function} [onRejected] Callback that will be invoked with a provided reason after the promise has been rejected * @param {Function} [onProgress] Callback that will be invoked with a provided value after the promise has been notified * @param {Object} [ctx] Context of the callbacks execution */ done : function(value, onFulfilled, onRejected, onProgress, ctx) { vow.when(value).done(onFulfilled, onRejected, onProgress, ctx); }, /** * Checks whether the given `value` is a promise-like object * * @param {*} value * @returns {Boolean} * * @example * ```js * vow.isPromise('something'); // returns false * vow.isPromise(vow.defer().promise()); // returns true * vow.isPromise({ then : function() { }); // returns true * ``` */ isPromise : function(value) { return isObject(value) && isFunction(value.then); }, /** * Coerces the given `value` to a promise, or returns the `value` if it's already a promise. * * @param {*} value * @returns {vow:Promise} */ cast : function(value) { return value && !!value._vow? value : vow.resolve(value); }, /** * Static equivalent to `promise.valueOf`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @returns {*} */ valueOf : function(value) { return value && isFunction(value.valueOf)? value.valueOf() : value; }, /** * Static equivalent to `promise.isFulfilled`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @returns {Boolean} */ isFulfilled : function(value) { return value && isFunction(value.isFulfilled)? value.isFulfilled() : true; }, /** * Static equivalent to `promise.isRejected`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @returns {Boolean} */ isRejected : function(value) { return value && isFunction(value.isRejected)? value.isRejected() : false; }, /** * Static equivalent to `promise.isResolved`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @returns {Boolean} */ isResolved : function(value) { return value && isFunction(value.isResolved)? value.isResolved() : true; }, /** * Returns a promise that has already been resolved with the given `value`. * If `value` is a promise, the returned promise will have `value`'s state. * * @param {*} value * @returns {vow:Promise} */ resolve : function(value) { var res = vow.defer(); res.resolve(value); return res.promise(); }, /** * Returns a promise that has already been fulfilled with the given `value`. * If `value` is a promise, the returned promise will be fulfilled with the fulfill/rejection value of `value`. * * @param {*} value * @returns {vow:Promise} */ fulfill : function(value) { var defer = vow.defer(), promise = defer.promise(); defer.resolve(value); return promise.isFulfilled()? promise : promise.then(null, function(reason) { return reason; }); }, /** * Returns a promise that has already been rejected with the given `reason`. * If `reason` is a promise, the returned promise will be rejected with the fulfill/rejection value of `reason`. * * @param {*} reason * @returns {vow:Promise} */ reject : function(reason) { var defer = vow.defer(); defer.reject(reason); return defer.promise(); }, /** * Invokes the given function `fn` with arguments `args` * * @param {Function} fn * @param {...*} [args] * @returns {vow:Promise} * * @example * ```js * var promise1 = vow.invoke(function(value) { * return value; * }, 'ok'), * promise2 = vow.invoke(function() { * throw Error(); * }); * * promise1.isFulfilled(); // true * promise1.valueOf(); // 'ok' * promise2.isRejected(); // true * promise2.valueOf(); // instance of Error * ``` */ invoke : function(fn, args) { var len = Math.max(arguments.length - 1, 0), callArgs; if(len) { // optimization for V8 callArgs = Array(len); var i = 0; while(i < len) { callArgs[i++] = arguments[i]; } } try { return vow.resolve(callArgs? fn.apply(global, callArgs) : fn.call(global)); } catch(e) { return vow.reject(e); } }, /** * Returns a promise, that will be fulfilled only after all the items in `iterable` are fulfilled. * If any of the `iterable` items gets rejected, the promise will be rejected. * * @param {Array|Object} iterable * @returns {vow:Promise} * * @example * with array: * ```js * var defer1 = vow.defer(), * defer2 = vow.defer(); * * vow.all([defer1.promise(), defer2.promise(), 3]) * .then(function(value) { * // value is "[1, 2, 3]" here * }); * * defer1.resolve(1); * defer2.resolve(2); * ``` * * @example * with object: * ```js * var defer1 = vow.defer(), * defer2 = vow.defer(); * * vow.all({ p1 : defer1.promise(), p2 : defer2.promise(), p3 : 3 }) * .then(function(value) { * // value is "{ p1 : 1, p2 : 2, p3 : 3 }" here * }); * * defer1.resolve(1); * defer2.resolve(2); * ``` */ all : function(iterable) { var defer = new Deferred(), isPromisesArray = isArray(iterable), keys = isPromisesArray? getArrayKeys(iterable) : getObjectKeys(iterable), len = keys.length, res = isPromisesArray? [] : {}; if(!len) { defer.resolve(res); return defer.promise(); } var i = len; vow._forEach( iterable, function(value, idx) { res[keys[idx]] = value; if(!--i) { defer.resolve(res); } }, defer.reject, defer.notify, defer, keys); return defer.promise(); }, /** * Returns a promise, that will be fulfilled only after all the items in `iterable` are resolved. * * @param {Array|Object} iterable * @returns {vow:Promise} * * @example * ```js * var defer1 = vow.defer(), * defer2 = vow.defer(); * * vow.allResolved([defer1.promise(), defer2.promise()]).spread(function(promise1, promise2) { * promise1.isRejected(); // returns true * promise1.valueOf(); // returns "'error'" * promise2.isFulfilled(); // returns true * promise2.valueOf(); // returns "'ok'" * }); * * defer1.reject('error'); * defer2.resolve('ok'); * ``` */ allResolved : function(iterable) { var defer = new Deferred(), isPromisesArray = isArray(iterable), keys = isPromisesArray? getArrayKeys(iterable) : getObjectKeys(iterable), i = keys.length, res = isPromisesArray? [] : {}; if(!i) { defer.resolve(res); return defer.promise(); } var onResolved = function() { --i || defer.resolve(iterable); }; vow._forEach( iterable, onResolved, onResolved, defer.notify, defer, keys); return defer.promise(); }, allSettled : function(iterable) { return vow.allResolved(iterable).then(function() { var isPromisesArray = isArray(iterable), keys = isPromisesArray? getArrayKeys(iterable) : getObjectKeys(iterable), res = isPromisesArray? [] : {}, len = keys.length, i = 0, key, value, item; while(i < len) { key = keys[i++]; promise = iterable[key]; value = promise.valueOf(); item = promise.isRejected()? { status : 'rejected', reason : value } : { status : 'fulfilled', value : value }; isPromisesArray? res.push(item) : res[key] = item; } return res; }); }, allPatiently : function(iterable) { return vow.allResolved(iterable).then(function() { var isPromisesArray = isArray(iterable), keys = isPromisesArray? getArrayKeys(iterable) : getObjectKeys(iterable), rejectedPromises, fulfilledPromises, len = keys.length, i = 0, key, promise; if(!len) { return isPromisesArray? [] : {}; } while(i < len) { key = keys[i++]; promise = iterable[key]; if(vow.isRejected(promise)) { rejectedPromises || (rejectedPromises = isPromisesArray? [] : {}); isPromisesArray? rejectedPromises.push(promise.valueOf()) : rejectedPromises[key] = promise.valueOf(); } else if(!rejectedPromises) { (fulfilledPromises || (fulfilledPromises = isPromisesArray? [] : {}))[key] = vow.valueOf(promise); } } if(rejectedPromises) { throw rejectedPromises; } return fulfilledPromises; }); }, /** * Returns a promise, that will be fulfilled if any of the items in `iterable` is fulfilled. * If all of the `iterable` items get rejected, the promise will be rejected (with the reason of the first rejected item). * * @param {Array} iterable * @returns {vow:Promise} */ any : function(iterable) { var defer = new Deferred(), len = iterable.length; if(!len) { defer.reject(Error()); return defer.promise(); } var i = 0, reason; vow._forEach( iterable, defer.resolve, function(e) { i || (reason = e); ++i === len && defer.reject(reason); }, defer.notify, defer); return defer.promise(); }, /** * Returns a promise, that will be fulfilled only when any of the items in `iterable` is fulfilled. * If any of the `iterable` items gets rejected, the promise will be rejected. * * @param {Array} iterable * @returns {vow:Promise} */ anyResolved : function(iterable) { var defer = new Deferred(), len = iterable.length; if(!len) { defer.reject(Error()); return defer.promise(); } vow._forEach( iterable, defer.resolve, defer.reject, defer.notify, defer); return defer.promise(); }, /** * Static equivalent to `promise.delay`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Number} delay * @returns {vow:Promise} */ delay : function(value, delay) { return vow.resolve(value).delay(delay); }, /** * Static equivalent to `promise.timeout`. * If `value` is not a promise, then `value` is treated as a fulfilled promise. * * @param {*} value * @param {Number} timeout * @returns {vow:Promise} */ timeout : function(value, timeout) { return vow.resolve(value).timeout(timeout); }, _forEach : function(promises, onFulfilled, onRejected, onProgress, ctx, keys) { var len = keys? keys.length : promises.length, i = 0; while(i < len) { vow.when( promises[keys? keys[i] : i], wrapOnFulfilled(onFulfilled, i), onRejected, onProgress, ctx); ++i; } }, TimedOutError : defineCustomErrorType('TimedOut') }; var defineAsGlobal = true; if(typeof module === 'object' && typeof module.exports === 'object') { module.exports = vow; defineAsGlobal = false; } if(typeof modules === 'object' && isFunction(modules.define)) { modules.define('vow', function(provide) { provide(vow); }); defineAsGlobal = false; } if(typeof define === 'function') { define(function(require, exports, module) { module.exports = vow; }); defineAsGlobal = false; } defineAsGlobal && (global.vow = vow); })(typeof window !== 'undefined'? window : global);