12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268 |
- /**
- * @module vow
- * @author Filatov Dmitry <dfilatov@yandex-team.ru>
- * @version 0.4.4
- * @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) {
- /**
- * @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 corresponding promise.
- *
- * @returns {vow:Promise}
- */
- promise : function() {
- return this._promise;
- },
- /**
- * Resolves corresponding promise with 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 corresponding promise with 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) {
- this._promise.isResolved() || this._promise._reject(reason);
- },
- /**
- * Notifies corresponding promise with 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,
- FULFILLED : 1,
- REJECTED : -1
- };
- /**
- * @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._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;
- 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);
- }
- };
- Promise.prototype = /** @lends Promise.prototype */ {
- /**
- * Returns value of fulfilled promise or reason in case of rejection.
- *
- * @returns {*}
- */
- valueOf : function() {
- return this._value;
- },
- /**
- * Returns `true` if promise is resolved.
- *
- * @returns {Boolean}
- */
- isResolved : function() {
- return this._status !== PROMISE_STATUS.PENDING;
- },
- /**
- * Returns `true` if promise is fulfilled.
- *
- * @returns {Boolean}
- */
- isFulfilled : function() {
- return this._status === PROMISE_STATUS.FULFILLED;
- },
- /**
- * Returns `true` if promise is rejected.
- *
- * @returns {Boolean}
- */
- isRejected : function() {
- return this._status === PROMISE_STATUS.REJECTED;
- },
- /**
- * Adds reactions to promise.
- *
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
- * @param {Object} [ctx] Context of callbacks execution
- * @returns {vow:Promise} A new promise, see https://github.com/promises-aplus/promises-spec for details
- */
- then : function(onFulfilled, onRejected, onProgress, ctx) {
- var defer = new Deferred();
- this._addCallbacks(defer, onFulfilled, onRejected, onProgress, ctx);
- return defer.promise();
- },
- /**
- * Adds rejection reaction only. It is shortcut for `promise.then(undefined, onRejected)`.
- *
- * @param {Function} onRejected Callback to be called with the value after promise has been rejected
- * @param {Object} [ctx] Context of callback execution
- * @returns {vow:Promise}
- */
- 'catch' : function(onRejected, ctx) {
- return this.then(undef, onRejected, ctx);
- },
- /**
- * Adds rejection reaction only. It is shortcut for `promise.then(null, onRejected)`. It's alias for `catch`.
- *
- * @param {Function} onRejected Callback to be called with the value after promise has been rejected
- * @param {Object} [ctx] Context of callback execution
- * @returns {vow:Promise}
- */
- fail : function(onRejected, ctx) {
- return this.then(undef, onRejected, ctx);
- },
- /**
- * Adds resolving reaction (to fulfillment and rejection both).
- *
- * @param {Function} onResolved Callback that to be called with the value after promise has been rejected
- * @param {Object} [ctx] Context of 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 progress reaction.
- *
- * @param {Function} onProgress Callback to be called with the value when promise has been notified
- * @param {Object} [ctx] Context of 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 `vow.all` and `vow.allResolved` methods.
- *
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Object} [ctx] Context of 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, throws it as an exception in a future turn of the event loop.
- *
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
- * @param {Object} [ctx] Context of 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 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(Error('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.PENDING) {
- return;
- }
- if(val === this) {
- this._reject(TypeError('Can\'t resolve promise with itself'));
- return;
- }
- if(val && !!val._vow) { // shortpath for vow.Promise
- val.then(
- this._resolve,
- 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.PENDING) {
- 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.PENDING) {
- return;
- }
- this._status = PROMISE_STATUS.REJECTED;
- this._value = reason;
- this._callCallbacks(this._rejectedCallbacks, reason);
- 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;
- }
- 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.PENDING) {
- 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();
- 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;
- }
- isResolved?
- defer.resolve(res) :
- defer.notify(res);
- }
- else {
- isResolved?
- isFulfilled?
- defer.resolve(arg) :
- defer.reject(arg) :
- defer.notify(arg);
- }
- }
- });
- }
- };
- /** @lends Promise */
- var staticMethods = {
- /**
- * Coerces 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 to be fulfilled only after all the items in `iterable` are fulfilled,
- * or to be rejected when any of the `iterable` is rejected.
- *
- * @param {Array|Object} iterable
- * @returns {vow:Promise}
- */
- all : function(iterable) {
- return vow.all(iterable);
- },
- /**
- * Returns a promise to be fulfilled only when any of the items in `iterable` are fulfilled,
- * or to be rejected when the first item is 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, returned promise will be adopted with the state of given promise.
- *
- * @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 given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
- * @param {Object} [ctx] Context of 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 given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} onRejected Callback that will to be invoked with the reason after promise has been rejected
- * @param {Object} [ctx] Context of callback execution
- * @returns {vow:Promise}
- */
- fail : function(value, onRejected, ctx) {
- return vow.when(value, undef, onRejected, ctx);
- },
- /**
- * Static equivalent to `promise.always`.
- * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} onResolved Callback that will to be invoked with the reason after promise has been resolved
- * @param {Object} [ctx] Context of callback execution
- * @returns {vow:Promise}
- */
- always : function(value, onResolved, ctx) {
- return vow.when(value).always(onResolved, ctx);
- },
- /**
- * Static equivalent to `promise.progress`.
- * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} onProgress Callback that will to be invoked with the reason after promise has been notified
- * @param {Object} [ctx] Context of callback execution
- * @returns {vow:Promise}
- */
- progress : function(value, onProgress, ctx) {
- return vow.when(value).progress(onProgress, ctx);
- },
- /**
- * Static equivalent to `promise.spread`.
- * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Object} [ctx] Context of 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 given `value` is not a promise, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
- * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
- * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
- * @param {Object} [ctx] Context of 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 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.isPromise(value)?
- value :
- vow.resolve(value);
- },
- /**
- * Static equivalent to `promise.valueOf`.
- * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @returns {*}
- */
- valueOf : function(value) {
- return value && isFunction(value.valueOf)? value.valueOf() : value;
- },
- /**
- * Static equivalent to `promise.isFulfilled`.
- * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @returns {Boolean}
- */
- isFulfilled : function(value) {
- return value && isFunction(value.isFulfilled)? value.isFulfilled() : true;
- },
- /**
- * Static equivalent to `promise.isRejected`.
- * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
- *
- * @param {*} value
- * @returns {Boolean}
- */
- isRejected : function(value) {
- return value && isFunction(value.isRejected)? value.isRejected() : false;
- },
- /**
- * Static equivalent to `promise.isResolved`.
- * If given `value` is not a promise, then `value` is equivalent to 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, returned promise will be adopted with the state of given promise.
- *
- * @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, returned promise will be fulfilled with fulfill/rejection value of given promise.
- *
- * @param {*} value
- * @returns {vow:Promise}
- */
- fulfill : function(value) {
- return vow.when(value, null, function(reason) {
- return reason;
- });
- },
- /**
- * Returns a promise that has already been rejected with the given `reason`.
- * If `reason` is a promise, returned promise will be rejected with fulfill/rejection value of given promise.
- *
- * @param {*} reason
- * @returns {vow:Promise}
- */
- reject : function(reason) {
- return vow.when(reason, function(val) {
- throw val;
- });
- },
- /**
- * Invokes a 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 to be fulfilled only after all the items in `iterable` are fulfilled,
- * or to be rejected when any of the `iterable` is 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() {
- if(!--i) {
- var j = 0;
- while(j < len) {
- res[keys[j]] = vow.valueOf(iterable[keys[j++]]);
- }
- defer.resolve(res);
- }
- },
- defer.reject,
- defer.notify,
- defer,
- keys);
- return defer.promise();
- },
- /**
- * Returns a promise to 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();
- },
- 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 to be fulfilled only when any of the items in `iterable` are fulfilled,
- * or to be rejected when all the items are 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 to be fulfilled only when any of the items in `iterable` are fulfilled,
- * or to be rejected when the first item is 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 given `value` is not a promise, then `value` is equivalent to 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 given `value` is not a promise, then `value` is equivalent to 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], onFulfilled, onRejected, onProgress, ctx);
- ++i;
- }
- }
- };
- var undef,
- nextTick = (function() {
- var fns = [],
- enqueueFn = function(fn) {
- return fns.push(fn) === 1;
- },
- callFns = function() {
- var fnsToCall = fns, i = 0, len = fns.length;
- fns = [];
- while(i < len) {
- fnsToCall[i++]();
- }
- };
- if(typeof setImmediate === 'function') { // ie10, nodejs >= 0.10
- return function(fn) {
- enqueueFn(fn) && setImmediate(callFns);
- };
- }
- if(typeof process === 'object' && process.nextTick) { // nodejs < 0.10
- return function(fn) {
- enqueueFn(fn) && process.nextTick(callFns);
- };
- }
- if(global.postMessage) { // modern browsers
- 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' + +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;
- };
- var defineAsGlobal = true;
- if(typeof exports === 'object') {
- module.exports = vow;
- defineAsGlobal = false;
- }
- if(typeof modules === 'object') {
- 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);
- })(this);
|