vow.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268
  1. /**
  2. * @module vow
  3. * @author Filatov Dmitry <dfilatov@yandex-team.ru>
  4. * @version 0.4.4
  5. * @license
  6. * Dual licensed under the MIT and GPL licenses:
  7. * * http://www.opensource.org/licenses/mit-license.php
  8. * * http://www.gnu.org/licenses/gpl.html
  9. */
  10. (function(global) {
  11. /**
  12. * @class Deferred
  13. * @exports vow:Deferred
  14. * @description
  15. * The `Deferred` class is used to encapsulate newly-created promise object along with functions that resolve, reject or notify it.
  16. */
  17. /**
  18. * @constructor
  19. * @description
  20. * You can use `vow.defer()` instead of using this constructor.
  21. *
  22. * `new vow.Deferred()` gives the same result as `vow.defer()`.
  23. */
  24. var Deferred = function() {
  25. this._promise = new Promise();
  26. };
  27. Deferred.prototype = /** @lends Deferred.prototype */{
  28. /**
  29. * Returns corresponding promise.
  30. *
  31. * @returns {vow:Promise}
  32. */
  33. promise : function() {
  34. return this._promise;
  35. },
  36. /**
  37. * Resolves corresponding promise with given `value`.
  38. *
  39. * @param {*} value
  40. *
  41. * @example
  42. * ```js
  43. * var defer = vow.defer(),
  44. * promise = defer.promise();
  45. *
  46. * promise.then(function(value) {
  47. * // value is "'success'" here
  48. * });
  49. *
  50. * defer.resolve('success');
  51. * ```
  52. */
  53. resolve : function(value) {
  54. this._promise.isResolved() || this._promise._resolve(value);
  55. },
  56. /**
  57. * Rejects corresponding promise with given `reason`.
  58. *
  59. * @param {*} reason
  60. *
  61. * @example
  62. * ```js
  63. * var defer = vow.defer(),
  64. * promise = defer.promise();
  65. *
  66. * promise.fail(function(reason) {
  67. * // reason is "'something is wrong'" here
  68. * });
  69. *
  70. * defer.reject('something is wrong');
  71. * ```
  72. */
  73. reject : function(reason) {
  74. this._promise.isResolved() || this._promise._reject(reason);
  75. },
  76. /**
  77. * Notifies corresponding promise with given `value`.
  78. *
  79. * @param {*} value
  80. *
  81. * @example
  82. * ```js
  83. * var defer = vow.defer(),
  84. * promise = defer.promise();
  85. *
  86. * promise.progress(function(value) {
  87. * // value is "'20%'", "'40%'" here
  88. * });
  89. *
  90. * defer.notify('20%');
  91. * defer.notify('40%');
  92. * ```
  93. */
  94. notify : function(value) {
  95. this._promise.isResolved() || this._promise._notify(value);
  96. }
  97. };
  98. var PROMISE_STATUS = {
  99. PENDING : 0,
  100. FULFILLED : 1,
  101. REJECTED : -1
  102. };
  103. /**
  104. * @class Promise
  105. * @exports vow:Promise
  106. * @description
  107. * The `Promise` class is used when you want to give to the caller something to subscribe to,
  108. * but not the ability to resolve or reject the deferred.
  109. */
  110. /**
  111. * @constructor
  112. * @param {Function} resolver See https://github.com/domenic/promises-unwrapping/blob/master/README.md#the-promise-constructor for details.
  113. * @description
  114. * You should use this constructor directly only if you are going to use `vow` as DOM Promises implementation.
  115. * In other case you should use `vow.defer()` and `defer.promise()` methods.
  116. * @example
  117. * ```js
  118. * function fetchJSON(url) {
  119. * return new vow.Promise(function(resolve, reject, notify) {
  120. * var xhr = new XMLHttpRequest();
  121. * xhr.open('GET', url);
  122. * xhr.responseType = 'json';
  123. * xhr.send();
  124. * xhr.onload = function() {
  125. * if(xhr.response) {
  126. * resolve(xhr.response);
  127. * }
  128. * else {
  129. * reject(new TypeError());
  130. * }
  131. * };
  132. * });
  133. * }
  134. * ```
  135. */
  136. var Promise = function(resolver) {
  137. this._value = undef;
  138. this._status = PROMISE_STATUS.PENDING;
  139. this._fulfilledCallbacks = [];
  140. this._rejectedCallbacks = [];
  141. this._progressCallbacks = [];
  142. if(resolver) { // NOTE: see https://github.com/domenic/promises-unwrapping/blob/master/README.md
  143. var _this = this,
  144. resolverFnLen = resolver.length;
  145. resolver(
  146. function(val) {
  147. _this.isResolved() || _this._resolve(val);
  148. },
  149. resolverFnLen > 1?
  150. function(reason) {
  151. _this.isResolved() || _this._reject(reason);
  152. } :
  153. undef,
  154. resolverFnLen > 2?
  155. function(val) {
  156. _this.isResolved() || _this._notify(val);
  157. } :
  158. undef);
  159. }
  160. };
  161. Promise.prototype = /** @lends Promise.prototype */ {
  162. /**
  163. * Returns value of fulfilled promise or reason in case of rejection.
  164. *
  165. * @returns {*}
  166. */
  167. valueOf : function() {
  168. return this._value;
  169. },
  170. /**
  171. * Returns `true` if promise is resolved.
  172. *
  173. * @returns {Boolean}
  174. */
  175. isResolved : function() {
  176. return this._status !== PROMISE_STATUS.PENDING;
  177. },
  178. /**
  179. * Returns `true` if promise is fulfilled.
  180. *
  181. * @returns {Boolean}
  182. */
  183. isFulfilled : function() {
  184. return this._status === PROMISE_STATUS.FULFILLED;
  185. },
  186. /**
  187. * Returns `true` if promise is rejected.
  188. *
  189. * @returns {Boolean}
  190. */
  191. isRejected : function() {
  192. return this._status === PROMISE_STATUS.REJECTED;
  193. },
  194. /**
  195. * Adds reactions to promise.
  196. *
  197. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  198. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  199. * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
  200. * @param {Object} [ctx] Context of callbacks execution
  201. * @returns {vow:Promise} A new promise, see https://github.com/promises-aplus/promises-spec for details
  202. */
  203. then : function(onFulfilled, onRejected, onProgress, ctx) {
  204. var defer = new Deferred();
  205. this._addCallbacks(defer, onFulfilled, onRejected, onProgress, ctx);
  206. return defer.promise();
  207. },
  208. /**
  209. * Adds rejection reaction only. It is shortcut for `promise.then(undefined, onRejected)`.
  210. *
  211. * @param {Function} onRejected Callback to be called with the value after promise has been rejected
  212. * @param {Object} [ctx] Context of callback execution
  213. * @returns {vow:Promise}
  214. */
  215. 'catch' : function(onRejected, ctx) {
  216. return this.then(undef, onRejected, ctx);
  217. },
  218. /**
  219. * Adds rejection reaction only. It is shortcut for `promise.then(null, onRejected)`. It's alias for `catch`.
  220. *
  221. * @param {Function} onRejected Callback to be called with the value after promise has been rejected
  222. * @param {Object} [ctx] Context of callback execution
  223. * @returns {vow:Promise}
  224. */
  225. fail : function(onRejected, ctx) {
  226. return this.then(undef, onRejected, ctx);
  227. },
  228. /**
  229. * Adds resolving reaction (to fulfillment and rejection both).
  230. *
  231. * @param {Function} onResolved Callback that to be called with the value after promise has been rejected
  232. * @param {Object} [ctx] Context of callback execution
  233. * @returns {vow:Promise}
  234. */
  235. always : function(onResolved, ctx) {
  236. var _this = this,
  237. cb = function() {
  238. return onResolved.call(this, _this);
  239. };
  240. return this.then(cb, cb, ctx);
  241. },
  242. /**
  243. * Adds progress reaction.
  244. *
  245. * @param {Function} onProgress Callback to be called with the value when promise has been notified
  246. * @param {Object} [ctx] Context of callback execution
  247. * @returns {vow:Promise}
  248. */
  249. progress : function(onProgress, ctx) {
  250. return this.then(undef, undef, onProgress, ctx);
  251. },
  252. /**
  253. * Like `promise.then`, but "spreads" the array into a variadic value handler.
  254. * It is useful with `vow.all` and `vow.allResolved` methods.
  255. *
  256. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  257. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  258. * @param {Object} [ctx] Context of callbacks execution
  259. * @returns {vow:Promise}
  260. *
  261. * @example
  262. * ```js
  263. * var defer1 = vow.defer(),
  264. * defer2 = vow.defer();
  265. *
  266. * vow.all([defer1.promise(), defer2.promise()]).spread(function(arg1, arg2) {
  267. * // arg1 is "1", arg2 is "'two'" here
  268. * });
  269. *
  270. * defer1.resolve(1);
  271. * defer2.resolve('two');
  272. * ```
  273. */
  274. spread : function(onFulfilled, onRejected, ctx) {
  275. return this.then(
  276. function(val) {
  277. return onFulfilled.apply(this, val);
  278. },
  279. onRejected,
  280. ctx);
  281. },
  282. /**
  283. * Like `then`, but terminates a chain of promises.
  284. * If the promise has been rejected, throws it as an exception in a future turn of the event loop.
  285. *
  286. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  287. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  288. * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
  289. * @param {Object} [ctx] Context of callbacks execution
  290. *
  291. * @example
  292. * ```js
  293. * var defer = vow.defer();
  294. * defer.reject(Error('Internal error'));
  295. * defer.promise().done(); // exception to be thrown
  296. * ```
  297. */
  298. done : function(onFulfilled, onRejected, onProgress, ctx) {
  299. this
  300. .then(onFulfilled, onRejected, onProgress, ctx)
  301. .fail(throwException);
  302. },
  303. /**
  304. * Returns a new promise that will be fulfilled in `delay` milliseconds if the promise is fulfilled,
  305. * or immediately rejected if promise is rejected.
  306. *
  307. * @param {Number} delay
  308. * @returns {vow:Promise}
  309. */
  310. delay : function(delay) {
  311. var timer,
  312. promise = this.then(function(val) {
  313. var defer = new Deferred();
  314. timer = setTimeout(
  315. function() {
  316. defer.resolve(val);
  317. },
  318. delay);
  319. return defer.promise();
  320. });
  321. promise.always(function() {
  322. clearTimeout(timer);
  323. });
  324. return promise;
  325. },
  326. /**
  327. * Returns a new promise that will be rejected in `timeout` milliseconds
  328. * if the promise is not resolved beforehand.
  329. *
  330. * @param {Number} timeout
  331. * @returns {vow:Promise}
  332. *
  333. * @example
  334. * ```js
  335. * var defer = vow.defer(),
  336. * promiseWithTimeout1 = defer.promise().timeout(50),
  337. * promiseWithTimeout2 = defer.promise().timeout(200);
  338. *
  339. * setTimeout(
  340. * function() {
  341. * defer.resolve('ok');
  342. * },
  343. * 100);
  344. *
  345. * promiseWithTimeout1.fail(function(reason) {
  346. * // promiseWithTimeout to be rejected in 50ms
  347. * });
  348. *
  349. * promiseWithTimeout2.then(function(value) {
  350. * // promiseWithTimeout to be fulfilled with "'ok'" value
  351. * });
  352. * ```
  353. */
  354. timeout : function(timeout) {
  355. var defer = new Deferred(),
  356. timer = setTimeout(
  357. function() {
  358. defer.reject(Error('timed out'));
  359. },
  360. timeout);
  361. this.then(
  362. function(val) {
  363. defer.resolve(val);
  364. },
  365. function(reason) {
  366. defer.reject(reason);
  367. });
  368. defer.promise().always(function() {
  369. clearTimeout(timer);
  370. });
  371. return defer.promise();
  372. },
  373. _vow : true,
  374. _resolve : function(val) {
  375. if(this._status !== PROMISE_STATUS.PENDING) {
  376. return;
  377. }
  378. if(val === this) {
  379. this._reject(TypeError('Can\'t resolve promise with itself'));
  380. return;
  381. }
  382. if(val && !!val._vow) { // shortpath for vow.Promise
  383. val.then(
  384. this._resolve,
  385. this._reject,
  386. this._notify,
  387. this);
  388. return;
  389. }
  390. if(isObject(val) || isFunction(val)) {
  391. var then;
  392. try {
  393. then = val.then;
  394. }
  395. catch(e) {
  396. this._reject(e);
  397. return;
  398. }
  399. if(isFunction(then)) {
  400. var _this = this,
  401. isResolved = false;
  402. try {
  403. then.call(
  404. val,
  405. function(val) {
  406. if(isResolved) {
  407. return;
  408. }
  409. isResolved = true;
  410. _this._resolve(val);
  411. },
  412. function(err) {
  413. if(isResolved) {
  414. return;
  415. }
  416. isResolved = true;
  417. _this._reject(err);
  418. },
  419. function(val) {
  420. _this._notify(val);
  421. });
  422. }
  423. catch(e) {
  424. isResolved || this._reject(e);
  425. }
  426. return;
  427. }
  428. }
  429. this._fulfill(val);
  430. },
  431. _fulfill : function(val) {
  432. if(this._status !== PROMISE_STATUS.PENDING) {
  433. return;
  434. }
  435. this._status = PROMISE_STATUS.FULFILLED;
  436. this._value = val;
  437. this._callCallbacks(this._fulfilledCallbacks, val);
  438. this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = undef;
  439. },
  440. _reject : function(reason) {
  441. if(this._status !== PROMISE_STATUS.PENDING) {
  442. return;
  443. }
  444. this._status = PROMISE_STATUS.REJECTED;
  445. this._value = reason;
  446. this._callCallbacks(this._rejectedCallbacks, reason);
  447. this._fulfilledCallbacks = this._rejectedCallbacks = this._progressCallbacks = undef;
  448. },
  449. _notify : function(val) {
  450. this._callCallbacks(this._progressCallbacks, val);
  451. },
  452. _addCallbacks : function(defer, onFulfilled, onRejected, onProgress, ctx) {
  453. if(onRejected && !isFunction(onRejected)) {
  454. ctx = onRejected;
  455. onRejected = undef;
  456. }
  457. else if(onProgress && !isFunction(onProgress)) {
  458. ctx = onProgress;
  459. onProgress = undef;
  460. }
  461. var cb;
  462. if(!this.isRejected()) {
  463. cb = { defer : defer, fn : isFunction(onFulfilled)? onFulfilled : undef, ctx : ctx };
  464. this.isFulfilled()?
  465. this._callCallbacks([cb], this._value) :
  466. this._fulfilledCallbacks.push(cb);
  467. }
  468. if(!this.isFulfilled()) {
  469. cb = { defer : defer, fn : onRejected, ctx : ctx };
  470. this.isRejected()?
  471. this._callCallbacks([cb], this._value) :
  472. this._rejectedCallbacks.push(cb);
  473. }
  474. if(this._status === PROMISE_STATUS.PENDING) {
  475. this._progressCallbacks.push({ defer : defer, fn : onProgress, ctx : ctx });
  476. }
  477. },
  478. _callCallbacks : function(callbacks, arg) {
  479. var len = callbacks.length;
  480. if(!len) {
  481. return;
  482. }
  483. var isResolved = this.isResolved(),
  484. isFulfilled = this.isFulfilled();
  485. nextTick(function() {
  486. var i = 0, cb, defer, fn;
  487. while(i < len) {
  488. cb = callbacks[i++];
  489. defer = cb.defer;
  490. fn = cb.fn;
  491. if(fn) {
  492. var ctx = cb.ctx,
  493. res;
  494. try {
  495. res = ctx? fn.call(ctx, arg) : fn(arg);
  496. }
  497. catch(e) {
  498. defer.reject(e);
  499. continue;
  500. }
  501. isResolved?
  502. defer.resolve(res) :
  503. defer.notify(res);
  504. }
  505. else {
  506. isResolved?
  507. isFulfilled?
  508. defer.resolve(arg) :
  509. defer.reject(arg) :
  510. defer.notify(arg);
  511. }
  512. }
  513. });
  514. }
  515. };
  516. /** @lends Promise */
  517. var staticMethods = {
  518. /**
  519. * Coerces given `value` to a promise, or returns the `value` if it's already a promise.
  520. *
  521. * @param {*} value
  522. * @returns {vow:Promise}
  523. */
  524. cast : function(value) {
  525. return vow.cast(value);
  526. },
  527. /**
  528. * Returns a promise to be fulfilled only after all the items in `iterable` are fulfilled,
  529. * or to be rejected when any of the `iterable` is rejected.
  530. *
  531. * @param {Array|Object} iterable
  532. * @returns {vow:Promise}
  533. */
  534. all : function(iterable) {
  535. return vow.all(iterable);
  536. },
  537. /**
  538. * Returns a promise to be fulfilled only when any of the items in `iterable` are fulfilled,
  539. * or to be rejected when the first item is rejected.
  540. *
  541. * @param {Array} iterable
  542. * @returns {vow:Promise}
  543. */
  544. race : function(iterable) {
  545. return vow.anyResolved(iterable);
  546. },
  547. /**
  548. * Returns a promise that has already been resolved with the given `value`.
  549. * If `value` is a promise, returned promise will be adopted with the state of given promise.
  550. *
  551. * @param {*} value
  552. * @returns {vow:Promise}
  553. */
  554. resolve : function(value) {
  555. return vow.resolve(value);
  556. },
  557. /**
  558. * Returns a promise that has already been rejected with the given `reason`.
  559. *
  560. * @param {*} reason
  561. * @returns {vow:Promise}
  562. */
  563. reject : function(reason) {
  564. return vow.reject(reason);
  565. }
  566. };
  567. for(var prop in staticMethods) {
  568. staticMethods.hasOwnProperty(prop) &&
  569. (Promise[prop] = staticMethods[prop]);
  570. }
  571. var vow = /** @exports vow */ {
  572. Deferred : Deferred,
  573. Promise : Promise,
  574. /**
  575. * Creates a new deferred. This method is a factory method for `vow:Deferred` class.
  576. * It's equivalent to `new vow.Deferred()`.
  577. *
  578. * @returns {vow:Deferred}
  579. */
  580. defer : function() {
  581. return new Deferred();
  582. },
  583. /**
  584. * Static equivalent to `promise.then`.
  585. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  586. *
  587. * @param {*} value
  588. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  589. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  590. * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
  591. * @param {Object} [ctx] Context of callbacks execution
  592. * @returns {vow:Promise}
  593. */
  594. when : function(value, onFulfilled, onRejected, onProgress, ctx) {
  595. return vow.cast(value).then(onFulfilled, onRejected, onProgress, ctx);
  596. },
  597. /**
  598. * Static equivalent to `promise.fail`.
  599. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  600. *
  601. * @param {*} value
  602. * @param {Function} onRejected Callback that will to be invoked with the reason after promise has been rejected
  603. * @param {Object} [ctx] Context of callback execution
  604. * @returns {vow:Promise}
  605. */
  606. fail : function(value, onRejected, ctx) {
  607. return vow.when(value, undef, onRejected, ctx);
  608. },
  609. /**
  610. * Static equivalent to `promise.always`.
  611. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  612. *
  613. * @param {*} value
  614. * @param {Function} onResolved Callback that will to be invoked with the reason after promise has been resolved
  615. * @param {Object} [ctx] Context of callback execution
  616. * @returns {vow:Promise}
  617. */
  618. always : function(value, onResolved, ctx) {
  619. return vow.when(value).always(onResolved, ctx);
  620. },
  621. /**
  622. * Static equivalent to `promise.progress`.
  623. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  624. *
  625. * @param {*} value
  626. * @param {Function} onProgress Callback that will to be invoked with the reason after promise has been notified
  627. * @param {Object} [ctx] Context of callback execution
  628. * @returns {vow:Promise}
  629. */
  630. progress : function(value, onProgress, ctx) {
  631. return vow.when(value).progress(onProgress, ctx);
  632. },
  633. /**
  634. * Static equivalent to `promise.spread`.
  635. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  636. *
  637. * @param {*} value
  638. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  639. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  640. * @param {Object} [ctx] Context of callbacks execution
  641. * @returns {vow:Promise}
  642. */
  643. spread : function(value, onFulfilled, onRejected, ctx) {
  644. return vow.when(value).spread(onFulfilled, onRejected, ctx);
  645. },
  646. /**
  647. * Static equivalent to `promise.done`.
  648. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  649. *
  650. * @param {*} value
  651. * @param {Function} [onFulfilled] Callback that will to be invoked with the value after promise has been fulfilled
  652. * @param {Function} [onRejected] Callback that will to be invoked with the reason after promise has been rejected
  653. * @param {Function} [onProgress] Callback that will to be invoked with the value after promise has been notified
  654. * @param {Object} [ctx] Context of callbacks execution
  655. */
  656. done : function(value, onFulfilled, onRejected, onProgress, ctx) {
  657. vow.when(value).done(onFulfilled, onRejected, onProgress, ctx);
  658. },
  659. /**
  660. * Checks whether the given `value` is a promise-like object
  661. *
  662. * @param {*} value
  663. * @returns {Boolean}
  664. *
  665. * @example
  666. * ```js
  667. * vow.isPromise('something'); // returns false
  668. * vow.isPromise(vow.defer().promise()); // returns true
  669. * vow.isPromise({ then : function() { }); // returns true
  670. * ```
  671. */
  672. isPromise : function(value) {
  673. return isObject(value) && isFunction(value.then);
  674. },
  675. /**
  676. * Coerces given `value` to a promise, or returns the `value` if it's already a promise.
  677. *
  678. * @param {*} value
  679. * @returns {vow:Promise}
  680. */
  681. cast : function(value) {
  682. return vow.isPromise(value)?
  683. value :
  684. vow.resolve(value);
  685. },
  686. /**
  687. * Static equivalent to `promise.valueOf`.
  688. * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
  689. *
  690. * @param {*} value
  691. * @returns {*}
  692. */
  693. valueOf : function(value) {
  694. return value && isFunction(value.valueOf)? value.valueOf() : value;
  695. },
  696. /**
  697. * Static equivalent to `promise.isFulfilled`.
  698. * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
  699. *
  700. * @param {*} value
  701. * @returns {Boolean}
  702. */
  703. isFulfilled : function(value) {
  704. return value && isFunction(value.isFulfilled)? value.isFulfilled() : true;
  705. },
  706. /**
  707. * Static equivalent to `promise.isRejected`.
  708. * If given `value` is not an instance of `vow.Promise`, then `value` is equivalent to fulfilled promise.
  709. *
  710. * @param {*} value
  711. * @returns {Boolean}
  712. */
  713. isRejected : function(value) {
  714. return value && isFunction(value.isRejected)? value.isRejected() : false;
  715. },
  716. /**
  717. * Static equivalent to `promise.isResolved`.
  718. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  719. *
  720. * @param {*} value
  721. * @returns {Boolean}
  722. */
  723. isResolved : function(value) {
  724. return value && isFunction(value.isResolved)? value.isResolved() : true;
  725. },
  726. /**
  727. * Returns a promise that has already been resolved with the given `value`.
  728. * If `value` is a promise, returned promise will be adopted with the state of given promise.
  729. *
  730. * @param {*} value
  731. * @returns {vow:Promise}
  732. */
  733. resolve : function(value) {
  734. var res = vow.defer();
  735. res.resolve(value);
  736. return res.promise();
  737. },
  738. /**
  739. * Returns a promise that has already been fulfilled with the given `value`.
  740. * If `value` is a promise, returned promise will be fulfilled with fulfill/rejection value of given promise.
  741. *
  742. * @param {*} value
  743. * @returns {vow:Promise}
  744. */
  745. fulfill : function(value) {
  746. return vow.when(value, null, function(reason) {
  747. return reason;
  748. });
  749. },
  750. /**
  751. * Returns a promise that has already been rejected with the given `reason`.
  752. * If `reason` is a promise, returned promise will be rejected with fulfill/rejection value of given promise.
  753. *
  754. * @param {*} reason
  755. * @returns {vow:Promise}
  756. */
  757. reject : function(reason) {
  758. return vow.when(reason, function(val) {
  759. throw val;
  760. });
  761. },
  762. /**
  763. * Invokes a given function `fn` with arguments `args`
  764. *
  765. * @param {Function} fn
  766. * @param {...*} [args]
  767. * @returns {vow:Promise}
  768. *
  769. * @example
  770. * ```js
  771. * var promise1 = vow.invoke(function(value) {
  772. * return value;
  773. * }, 'ok'),
  774. * promise2 = vow.invoke(function() {
  775. * throw Error();
  776. * });
  777. *
  778. * promise1.isFulfilled(); // true
  779. * promise1.valueOf(); // 'ok'
  780. * promise2.isRejected(); // true
  781. * promise2.valueOf(); // instance of Error
  782. * ```
  783. */
  784. invoke : function(fn, args) {
  785. var len = Math.max(arguments.length - 1, 0),
  786. callArgs;
  787. if(len) { // optimization for V8
  788. callArgs = Array(len);
  789. var i = 0;
  790. while(i < len) {
  791. callArgs[i++] = arguments[i];
  792. }
  793. }
  794. try {
  795. return vow.resolve(callArgs?
  796. fn.apply(global, callArgs) :
  797. fn.call(global));
  798. }
  799. catch(e) {
  800. return vow.reject(e);
  801. }
  802. },
  803. /**
  804. * Returns a promise to be fulfilled only after all the items in `iterable` are fulfilled,
  805. * or to be rejected when any of the `iterable` is rejected.
  806. *
  807. * @param {Array|Object} iterable
  808. * @returns {vow:Promise}
  809. *
  810. * @example
  811. * with array:
  812. * ```js
  813. * var defer1 = vow.defer(),
  814. * defer2 = vow.defer();
  815. *
  816. * vow.all([defer1.promise(), defer2.promise(), 3])
  817. * .then(function(value) {
  818. * // value is "[1, 2, 3]" here
  819. * });
  820. *
  821. * defer1.resolve(1);
  822. * defer2.resolve(2);
  823. * ```
  824. *
  825. * @example
  826. * with object:
  827. * ```js
  828. * var defer1 = vow.defer(),
  829. * defer2 = vow.defer();
  830. *
  831. * vow.all({ p1 : defer1.promise(), p2 : defer2.promise(), p3 : 3 })
  832. * .then(function(value) {
  833. * // value is "{ p1 : 1, p2 : 2, p3 : 3 }" here
  834. * });
  835. *
  836. * defer1.resolve(1);
  837. * defer2.resolve(2);
  838. * ```
  839. */
  840. all : function(iterable) {
  841. var defer = new Deferred(),
  842. isPromisesArray = isArray(iterable),
  843. keys = isPromisesArray?
  844. getArrayKeys(iterable) :
  845. getObjectKeys(iterable),
  846. len = keys.length,
  847. res = isPromisesArray? [] : {};
  848. if(!len) {
  849. defer.resolve(res);
  850. return defer.promise();
  851. }
  852. var i = len;
  853. vow._forEach(
  854. iterable,
  855. function() {
  856. if(!--i) {
  857. var j = 0;
  858. while(j < len) {
  859. res[keys[j]] = vow.valueOf(iterable[keys[j++]]);
  860. }
  861. defer.resolve(res);
  862. }
  863. },
  864. defer.reject,
  865. defer.notify,
  866. defer,
  867. keys);
  868. return defer.promise();
  869. },
  870. /**
  871. * Returns a promise to be fulfilled only after all the items in `iterable` are resolved.
  872. *
  873. * @param {Array|Object} iterable
  874. * @returns {vow:Promise}
  875. *
  876. * @example
  877. * ```js
  878. * var defer1 = vow.defer(),
  879. * defer2 = vow.defer();
  880. *
  881. * vow.allResolved([defer1.promise(), defer2.promise()]).spread(function(promise1, promise2) {
  882. * promise1.isRejected(); // returns true
  883. * promise1.valueOf(); // returns "'error'"
  884. * promise2.isFulfilled(); // returns true
  885. * promise2.valueOf(); // returns "'ok'"
  886. * });
  887. *
  888. * defer1.reject('error');
  889. * defer2.resolve('ok');
  890. * ```
  891. */
  892. allResolved : function(iterable) {
  893. var defer = new Deferred(),
  894. isPromisesArray = isArray(iterable),
  895. keys = isPromisesArray?
  896. getArrayKeys(iterable) :
  897. getObjectKeys(iterable),
  898. i = keys.length,
  899. res = isPromisesArray? [] : {};
  900. if(!i) {
  901. defer.resolve(res);
  902. return defer.promise();
  903. }
  904. var onResolved = function() {
  905. --i || defer.resolve(iterable);
  906. };
  907. vow._forEach(
  908. iterable,
  909. onResolved,
  910. onResolved,
  911. defer.notify,
  912. defer,
  913. keys);
  914. return defer.promise();
  915. },
  916. allPatiently : function(iterable) {
  917. return vow.allResolved(iterable).then(function() {
  918. var isPromisesArray = isArray(iterable),
  919. keys = isPromisesArray?
  920. getArrayKeys(iterable) :
  921. getObjectKeys(iterable),
  922. rejectedPromises, fulfilledPromises,
  923. len = keys.length, i = 0, key, promise;
  924. if(!len) {
  925. return isPromisesArray? [] : {};
  926. }
  927. while(i < len) {
  928. key = keys[i++];
  929. promise = iterable[key];
  930. if(vow.isRejected(promise)) {
  931. rejectedPromises || (rejectedPromises = isPromisesArray? [] : {});
  932. isPromisesArray?
  933. rejectedPromises.push(promise.valueOf()) :
  934. rejectedPromises[key] = promise.valueOf();
  935. }
  936. else if(!rejectedPromises) {
  937. (fulfilledPromises || (fulfilledPromises = isPromisesArray? [] : {}))[key] = vow.valueOf(promise);
  938. }
  939. }
  940. if(rejectedPromises) {
  941. throw rejectedPromises;
  942. }
  943. return fulfilledPromises;
  944. });
  945. },
  946. /**
  947. * Returns a promise to be fulfilled only when any of the items in `iterable` are fulfilled,
  948. * or to be rejected when all the items are rejected (with the reason of the first rejected item).
  949. *
  950. * @param {Array} iterable
  951. * @returns {vow:Promise}
  952. */
  953. any : function(iterable) {
  954. var defer = new Deferred(),
  955. len = iterable.length;
  956. if(!len) {
  957. defer.reject(Error());
  958. return defer.promise();
  959. }
  960. var i = 0, reason;
  961. vow._forEach(
  962. iterable,
  963. defer.resolve,
  964. function(e) {
  965. i || (reason = e);
  966. ++i === len && defer.reject(reason);
  967. },
  968. defer.notify,
  969. defer);
  970. return defer.promise();
  971. },
  972. /**
  973. * Returns a promise to be fulfilled only when any of the items in `iterable` are fulfilled,
  974. * or to be rejected when the first item is rejected.
  975. *
  976. * @param {Array} iterable
  977. * @returns {vow:Promise}
  978. */
  979. anyResolved : function(iterable) {
  980. var defer = new Deferred(),
  981. len = iterable.length;
  982. if(!len) {
  983. defer.reject(Error());
  984. return defer.promise();
  985. }
  986. vow._forEach(
  987. iterable,
  988. defer.resolve,
  989. defer.reject,
  990. defer.notify,
  991. defer);
  992. return defer.promise();
  993. },
  994. /**
  995. * Static equivalent to `promise.delay`.
  996. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  997. *
  998. * @param {*} value
  999. * @param {Number} delay
  1000. * @returns {vow:Promise}
  1001. */
  1002. delay : function(value, delay) {
  1003. return vow.resolve(value).delay(delay);
  1004. },
  1005. /**
  1006. * Static equivalent to `promise.timeout`.
  1007. * If given `value` is not a promise, then `value` is equivalent to fulfilled promise.
  1008. *
  1009. * @param {*} value
  1010. * @param {Number} timeout
  1011. * @returns {vow:Promise}
  1012. */
  1013. timeout : function(value, timeout) {
  1014. return vow.resolve(value).timeout(timeout);
  1015. },
  1016. _forEach : function(promises, onFulfilled, onRejected, onProgress, ctx, keys) {
  1017. var len = keys? keys.length : promises.length,
  1018. i = 0;
  1019. while(i < len) {
  1020. vow.when(promises[keys? keys[i] : i], onFulfilled, onRejected, onProgress, ctx);
  1021. ++i;
  1022. }
  1023. }
  1024. };
  1025. var undef,
  1026. nextTick = (function() {
  1027. var fns = [],
  1028. enqueueFn = function(fn) {
  1029. return fns.push(fn) === 1;
  1030. },
  1031. callFns = function() {
  1032. var fnsToCall = fns, i = 0, len = fns.length;
  1033. fns = [];
  1034. while(i < len) {
  1035. fnsToCall[i++]();
  1036. }
  1037. };
  1038. if(typeof setImmediate === 'function') { // ie10, nodejs >= 0.10
  1039. return function(fn) {
  1040. enqueueFn(fn) && setImmediate(callFns);
  1041. };
  1042. }
  1043. if(typeof process === 'object' && process.nextTick) { // nodejs < 0.10
  1044. return function(fn) {
  1045. enqueueFn(fn) && process.nextTick(callFns);
  1046. };
  1047. }
  1048. if(global.postMessage) { // modern browsers
  1049. var isPostMessageAsync = true;
  1050. if(global.attachEvent) {
  1051. var checkAsync = function() {
  1052. isPostMessageAsync = false;
  1053. };
  1054. global.attachEvent('onmessage', checkAsync);
  1055. global.postMessage('__checkAsync', '*');
  1056. global.detachEvent('onmessage', checkAsync);
  1057. }
  1058. if(isPostMessageAsync) {
  1059. var msg = '__promise' + +new Date,
  1060. onMessage = function(e) {
  1061. if(e.data === msg) {
  1062. e.stopPropagation && e.stopPropagation();
  1063. callFns();
  1064. }
  1065. };
  1066. global.addEventListener?
  1067. global.addEventListener('message', onMessage, true) :
  1068. global.attachEvent('onmessage', onMessage);
  1069. return function(fn) {
  1070. enqueueFn(fn) && global.postMessage(msg, '*');
  1071. };
  1072. }
  1073. }
  1074. var doc = global.document;
  1075. if('onreadystatechange' in doc.createElement('script')) { // ie6-ie8
  1076. var createScript = function() {
  1077. var script = doc.createElement('script');
  1078. script.onreadystatechange = function() {
  1079. script.parentNode.removeChild(script);
  1080. script = script.onreadystatechange = null;
  1081. callFns();
  1082. };
  1083. (doc.documentElement || doc.body).appendChild(script);
  1084. };
  1085. return function(fn) {
  1086. enqueueFn(fn) && createScript();
  1087. };
  1088. }
  1089. return function(fn) { // old browsers
  1090. enqueueFn(fn) && setTimeout(callFns, 0);
  1091. };
  1092. })(),
  1093. throwException = function(e) {
  1094. nextTick(function() {
  1095. throw e;
  1096. });
  1097. },
  1098. isFunction = function(obj) {
  1099. return typeof obj === 'function';
  1100. },
  1101. isObject = function(obj) {
  1102. return obj !== null && typeof obj === 'object';
  1103. },
  1104. toStr = Object.prototype.toString,
  1105. isArray = Array.isArray || function(obj) {
  1106. return toStr.call(obj) === '[object Array]';
  1107. },
  1108. getArrayKeys = function(arr) {
  1109. var res = [],
  1110. i = 0, len = arr.length;
  1111. while(i < len) {
  1112. res.push(i++);
  1113. }
  1114. return res;
  1115. },
  1116. getObjectKeys = Object.keys || function(obj) {
  1117. var res = [];
  1118. for(var i in obj) {
  1119. obj.hasOwnProperty(i) && res.push(i);
  1120. }
  1121. return res;
  1122. };
  1123. var defineAsGlobal = true;
  1124. if(typeof exports === 'object') {
  1125. module.exports = vow;
  1126. defineAsGlobal = false;
  1127. }
  1128. if(typeof modules === 'object') {
  1129. modules.define('vow', function(provide) {
  1130. provide(vow);
  1131. });
  1132. defineAsGlobal = false;
  1133. }
  1134. if(typeof define === 'function') {
  1135. define(function(require, exports, module) {
  1136. module.exports = vow;
  1137. });
  1138. defineAsGlobal = false;
  1139. }
  1140. defineAsGlobal && (global.vow = vow);
  1141. })(this);