pax_global_header00006660000000000000000000000064125667104140014520gustar00rootroot0000000000000052 comment=5773bcb8e6c27b531e366cd247b83b8cbf7bc989 kew-0.7.0/000077500000000000000000000000001256671041400123125ustar00rootroot00000000000000kew-0.7.0/.gitignore000066400000000000000000000001401256671041400142750ustar00rootroot00000000000000lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results node_modules npm-debug.logkew-0.7.0/.travis.yml000066400000000000000000000000621256671041400144210ustar00rootroot00000000000000language: node_js node_js: - "0.12" - "0.10" kew-0.7.0/LICENSE.TXT000066400000000000000000000242151256671041400140010ustar00rootroot00000000000000Copyright 2012 The Obvious Corporation. http://obvious.com/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ------------------------------------------------------------------------- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS kew-0.7.0/README.md000066400000000000000000000230371256671041400135760ustar00rootroot00000000000000kew: a lightweight (and super fast) promise/deferred framework for node.js ================================== [![Build Status](https://travis-ci.org/Medium/kew.svg)](https://travis-ci.org/Medium/kew) **kew** is a lightweight promise framework with an aim of providing a base set of functionality similar to that provided by the [Q library](https://github.com/kriskowal/q "Q"). A few answers (for a few questions) ------- *Why'd we write it?* During our initial usage of **Q** we found that it was consuming 80% of the cpu under load (primarily in chained database callbacks). We spent some time looking at patching **Q** and ultimately found that creating our own lightweight library for server-usage would suit our needs better than figuring out how to make a large cross-platform library more performant on one very specific platform. *So this does everything Q does?* Nope! **Q** is still an awesome library and does *way* more than **kew**. We support a tiny subset of the **Q** functionality (the subset that we happen to use in our actual use cases). What are Promises? ------- At its core, a *Promise* is a promise to return a value at some point in the future. A *Promise* represents a value that will be (or may return an error if something goes wrong). *Promises* heavily reduce the complexity of asynchronous coding in node.js-like environments. Example: ```javascript // assuming the getUrlContent() function exists and retrieves the content of a url var htmlPromise = getUrlContent(myUrl) // we can then filter that through an http parser (our imaginary parseHtml() function) asynchronously (or maybe synchronously, who knows) var tagsPromise = htmlPromise.then(parseHtml) // and then filter it through another function (getLinks()) which retrieves only the link tags var linksPromise = tagsPromise.then(getLinks) // and then parses the actual urls from the links (using parseUrlsFromLinks()) var urlsPromise = linksPromise.then(parseUrlsFromLinks) // finally, we have a promise that should only provide us with the urls and will run once all the previous steps have ran urlsPromise.then(function (urls) { // do something with the urls }) ``` How do I use **kew**? ------- As a precursor to all the examples, the following code must be at the top of your page: ```javascript var Q = require('kew') ``` ### Convert a literal into a promise The easiest way to start a promise chain is by creating a new promise with a specified literal using Q.resolve() or Q.reject() ```javascript // create a promise which passes a value to the next then() call var successPromise = Q.resolve(val) // create a promise which throws an error to be caught by the next fail() call var failPromise = Q.reject(err) ``` In addition, you can create deferreds which can be used if you need to create a promise but resolve it later: ```javascript // create the deferreds var successDefer = Q.defer() var failDefer = Q.defer() // resolve or reject the defers in 1 second setTimeout(function () { successDefer.resolve("ok") failDefer.reject(new Error("this failed")) }, 1000) // extract promises from the deferreds var successPromise = successDefer.promise var failPromise = failDefer.promise ``` If you have a node-style callback (taking an **Error** as the first parameter and a response as the second), you can call the magic `makeNodeResolver()` function on a defer to allow the defer to handle the callbacks: ```javascript // create the deferred var defer = Q.defer() // some node-style function getObjectFromDatabase(myObjectId, defer.makeNodeResolver()) // grab the output defer.promise .then(function (obj) { // successfully retrieved the object }) .fail(function (e) { // failed retrieving the object }) ``` ### Handling successful results with `.then()` When a promise is resolved, you may call the `.then()` method to retrieve the value of the promise: ```javascript promise.then(function (result) { // do something with the result here }) ``` `.then()` will in turn return a promise which will return the results of whatever it returns (asynchronously or not), allowing it to be chained indefinitely: ```javascript Q.resolve('a') .then(function (result) { return result + 'b' }) .then(function (result) { return result + 'c' }) .then(function (result) { // result should be 'abc' }) ``` In addition, `.then()` calls may return promises themselves, allowing for complex nesting of asynchronous calls in a flat manner: ```javascript var htmlPromise = getUrlContent(myUrl) var tagsPromise = htmlPromise.then(function (html) { if (!validHtml(html)) throw new Error("Invalid HTML") // pretend that parseHtml() returns a promise and is asynchronous return parseHtml(html) }) ``` ### Handling errors with `.fail()` If a promise is rejected for some reason, you may handle the failure case with the `.fail()` function: ```javascript getObjectPromise .fail(function (e) { console.error("Failed to retrieve object", e) }) ``` Like `.then()`, `.fail()` also returns a promise. If the `.fail()` call does not throw an error, it will pass the return value of the `.fail()` handler to any `.then()` calls chained to it: ```javascript getObjectPromise .fail(function (e) { return retryGetObject(objId) }) .then(function (obj) { // yay, we received an object }) .fail(function (e) { // the retry failed :( console.error("Retrieving the object '" + objId + "' failed") }) }) ``` If you've reached the end of your promise chain, you may call `.end()` which signifies that the promise chain is ended and any errors should be thrown in whatever scope the code is currently in: ```javascript getObjectPromise // this will throw an error to the uncaught exception handler if the getObjectPromise call is asynchronous .end() ``` ### `.fin()` when things are finished You may attach a handler to a promise which will be ran regardless of whether the promise was resolved or rejected (but will only run upon completion). This is useful in the cases where you may have set up resources to run a request and wish to tear them down afterwards. `.fin()` will return the promise it is called upon: ```javascript var connection = db.connect() var itemPromise = db.getItem(itemId) .fin(function () { db.close() }) ``` Other utility methods ------- ### `.all()` for many things If you're waiting for multiple promises to return, you may pass them (mixed in with literals if you desire) into `.all()` which will create a promise that resolves successfully with an array of the results of the promises: ```javascript var promises = [] promises.push(getUrlContent(url1)) promises.push(getUrlContent(url2)) promises.push(getUrlContent(url3)) Q.all(promises) .then(function (content) { // content[0] === content for url 1 // content[1] === content for url 2 // content[2] === content for url 3 }) ``` If any of the promises fail, Q.all will fail as well (so make sure to guard your promises with a `.fail()` call beforehand if you don't care whether they succeed or not): ```javascript var promises = [] promises.push(getUrlContent(url1)) promises.push(getUrlContent(url2)) promises.push(getUrlContent(url3)) Q.all(promises) .fail(function (e) { console.log("Failed retrieving a url", e) }) ``` ### `.delay()` for future promises If you need a little bit of delay (such as retrying a method call to a service that is "eventually consistent") before doing something else, ``Q.delay()`` is your friend: ```javascript getUrlContent(url1) .fail(function () { // Retry again after 200 milisseconds return Q.delay(200).then(function () { return getUrlContent(url1) }) }) ``` If two arguments are passed, the first will be used as the return value, and the second will be the delay in milliseconds. ```javascript Q.delay(obj, 20).then(function (result) { console.log(result) // logs `obj` after 20ms }) ``` ### `.fcall()` for delaying a function invocation until the next tick: ```javascript // Assume someFn() is a synchronous 2 argument function you want to delay. Q.fcall(someFn, arg1, arg2) .then(function (result) { console.log('someFn(' + arg1 + ', ' + arg2 + ') = ' + result) }) ``` You can also use ``Q.fcall()`` with functions that return promises. ### `.ncall()` and `.nfcall()` for Node.js callbacks ``Q.nfcall()`` can be used to convert node-style callbacks into promises: ```javascript Q.nfcall(fs.writeFile, '/tmp/myFile', 'content') .then(function () { console.log('File written successfully') }) .fail(function (err) { console.log('Failed to write file', err) }) ``` If your Node-style callback needs a `this` context, you can use `Q.ncall`: ```js Q.ncall(redis.del, redis, 'my-key') .then(function () { return true }) .fail(function () { return false }) ``` ### `.spread()` for arrays of promises ``()`` can be used to convert node-style callbacks into promises: ```javascript Q.nfcall(function () { return ['a', Q.resolve('b')] }) .spread(function (a, b) { // ... }) ``` Contributing ------------ Questions, comments, bug reports, and pull requests are all welcome. Submit them at [the project on GitHub](https://github.com/Obvious/kew/). Bug reports that include steps-to-reproduce (including code) are the best. Even better, make them in the form of pull requests that update the test suite. Thanks! Author ------ [Jeremy Stanley](https://github.com/azulus) supported by [The Obvious Corporation](http://obvious.com/). License ------- Copyright 2013 [The Obvious Corporation](http://obvious.com/). Licensed under the Apache License, Version 2.0. See the top-level file `LICENSE.TXT` and (http://www.apache.org/licenses/LICENSE-2.0). kew-0.7.0/kew.js000066400000000000000000000534751256671041400134540ustar00rootroot00000000000000 /** * An object representing a "promise" for a future value * * @param {?function(T, ?)=} onSuccess a function to handle successful * resolution of this promise * @param {?function(!Error, ?)=} onFail a function to handle failed * resolution of this promise * @constructor * @template T */ function Promise(onSuccess, onFail) { this.promise = this this._isPromise = true this._successFn = onSuccess this._failFn = onFail this._scope = this this._boundArgs = null this._hasContext = false this._nextContext = undefined this._currentContext = undefined } /** * @param {function()} callback */ function nextTick (callback) { callback() } if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { nextTick = process.nextTick } /** * All callback execution should go through this function. While the * implementation below is simple, it can be replaced with more sophisticated * implementations that enforce QoS on the event loop. * * @param {Promise} defer * @param {Function} callback * @param {Object|undefined} scope * @param {Array} args */ function nextTickCallback (defer, callback, scope, args) { try { defer.resolve(callback.apply(scope, args)) } catch (thrown) { defer.reject(thrown) } } /** * Used for accessing the nextTick function from outside the kew module. * * @return {Function} */ function getNextTickFunction () { return nextTick } /** * Used for overriding the nextTick function from outside the kew module so that * the user can plug and play lower level schedulers * @param {!Function} fn */ function setNextTickFunction (fn) { nextTick = fn } /** * Keep track of the number of promises that are rejected along side * the number of rejected promises we call _failFn on so we can look * for leaked rejections. * @constructor */ function PromiseStats() { /** @type {number} */ this.errorsEmitted = 0 /** @type {number} */ this.errorsHandled = 0 } var stats = new PromiseStats() Promise.prototype._handleError = function () { if (!this._errorHandled) { stats.errorsHandled++ this._errorHandled = true } } /** * Specify that the current promise should have a specified context * @param {*} context context * @private */ Promise.prototype._useContext = function (context) { this._nextContext = this._currentContext = context this._hasContext = true return this } Promise.prototype.clearContext = function () { this._hasContext = false this._nextContext = undefined return this } /** * Set the context for all promise handlers to follow * * NOTE(dpup): This should be considered deprecated. It does not do what most * people would expect. The context will be passed as a second argument to all * subsequent callbacks. * * @param {*} context An arbitrary context */ Promise.prototype.setContext = function (context) { this._nextContext = context this._hasContext = true return this } /** * Get the context for a promise * @return {*} the context set by setContext */ Promise.prototype.getContext = function () { return this._nextContext } /** * Resolve this promise with a specified value * * @param {*=} data */ Promise.prototype.resolve = function (data) { if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice") var i if (data && isPromise(data)) { this._child = data if (this._promises) { for (i = 0; i < this._promises.length; i += 1) { data._chainPromise(this._promises[i]) } delete this._promises } if (this._onComplete) { for (i = 0; i < this._onComplete.length; i+= 1) { data.fin(this._onComplete[i]) } delete this._onComplete } } else if (data && isPromiseLike(data)) { data.then( function(data) { this.resolve(data) }.bind(this), function(err) { this.reject(err) }.bind(this) ) } else { this._hasData = true this._data = data if (this._onComplete) { for (i = 0; i < this._onComplete.length; i++) { this._onComplete[i]() } } if (this._promises) { for (i = 0; i < this._promises.length; i += 1) { this._promises[i]._useContext(this._nextContext) this._promises[i]._withInput(data) } delete this._promises } } } /** * Reject this promise with an error * * @param {!Error} e */ Promise.prototype.reject = function (e) { if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice") var i this._error = e stats.errorsEmitted++ if (this._ended) { this._handleError() process.nextTick(function onPromiseThrow() { throw e }) } if (this._onComplete) { for (i = 0; i < this._onComplete.length; i++) { this._onComplete[i]() } } if (this._promises) { this._handleError() for (i = 0; i < this._promises.length; i += 1) { this._promises[i]._useContext(this._nextContext) this._promises[i]._withError(e) } delete this._promises } } /** * Provide a callback to be called whenever this promise successfully * resolves. Allows for an optional second callback to handle the failure * case. * * @param {?function(this:void, T, ?): RESULT|undefined} onSuccess * @param {?function(this:void, !Error, ?): RESULT=} onFail * @return {!Promise.} returns a new promise with the output of the onSuccess or * onFail handler * @template RESULT */ Promise.prototype.then = function (onSuccess, onFail) { var promise = new Promise(onSuccess, onFail) if (this._nextContext) promise._useContext(this._nextContext) if (this._child) this._child._chainPromise(promise) else this._chainPromise(promise) return promise } /** * Provide a callback to be called whenever this promise successfully * resolves. The callback will be executed in the context of the provided scope. * * @param {function(this:SCOPE, ...): RESULT} onSuccess * @param {SCOPE} scope Object whose context callback will be executed in. * @param {...*} var_args Additional arguments to be passed to the promise callback. * @return {!Promise.} returns a new promise with the output of the onSuccess * @template SCOPE, RESULT */ Promise.prototype.thenBound = function (onSuccess, scope, var_args) { var promise = new Promise(onSuccess) if (this._nextContext) promise._useContext(this._nextContext) promise._scope = scope if (arguments.length > 2) { promise._boundArgs = Array.prototype.slice.call(arguments, 2) } // Chaining must happen after setting args and scope since it may fire callback. if (this._child) this._child._chainPromise(promise) else this._chainPromise(promise) return promise } /** * Provide a callback to be called whenever this promise is rejected * * @param {function(this:void, !Error, ?)} onFail * @return {!Promise.} returns a new promise with the output of the onFail handler */ Promise.prototype.fail = function (onFail) { return this.then(null, onFail) } /** * Provide a callback to be called whenever this promise is rejected. * The callback will be executed in the context of the provided scope. * * @param {function(this:SCOPE, ...)} onFail * @param {SCOPE} scope Object whose context callback will be executed in. * @param {...?} var_args * @return {!Promise.} returns a new promise with the output of the onSuccess * @template SCOPE */ Promise.prototype.failBound = function (onFail, scope, var_args) { var promise = new Promise(null, onFail) if (this._nextContext) promise._useContext(this._nextContext) promise._scope = scope if (arguments.length > 2) { promise._boundArgs = Array.prototype.slice.call(arguments, 2) } // Chaining must happen after setting args and scope since it may fire callback. if (this._child) this._child._chainPromise(promise) else this._chainPromise(promise) return promise } /** * Spread a promises outputs to the functions arguments. * @param {?function(this:void, ...): RESULT|undefined} onSuccess * @return {!Promise.} returns a new promise with the output of the onSuccess or * onFail handler * @template RESULT */ Promise.prototype.spread = function (onSuccess) { return this.then(allInternal) .then(function (array) { return onSuccess.apply(null, array) }) } /** * Spread a promises outputs to the functions arguments. * @param {function(this:SCOPE, ...): RESULT} onSuccess * @param {SCOPE} scope Object whose context callback will be executed in. * @param {...*} var_args Additional arguments to be passed to the promise callback. * @return {!Promise.} returns a new promise with the output of the onSuccess * @template SCOPE, RESULT */ Promise.prototype.spreadBound = function (onSuccess, scope, var_args) { var args = Array.prototype.slice.call(arguments, 2) return this.then(allInternal) .then(function (array) { return onSuccess.apply(scope, args.concat(array)) }) } /** * Provide a callback to be called whenever this promise is either resolved * or rejected. * * @param {function()} onComplete * @return {!Promise.} returns the current promise */ Promise.prototype.fin = function (onComplete) { if (this._hasData || this._error) { onComplete() return this } if (this._child) { this._child.fin(onComplete) } else { if (!this._onComplete) this._onComplete = [onComplete] else this._onComplete.push(onComplete) } return this } /** * Mark this promise as "ended". If the promise is rejected, this will throw an * error in whatever scope it happens to be in * * @return {!Promise.} returns the current promise * @deprecated Prefer done(), because it's consistent with Q. */ Promise.prototype.end = function () { this._end() return this } /** * Mark this promise as "ended". * @private */ Promise.prototype._end = function () { if (this._error) { this._handleError() throw this._error } this._ended = true return this } /** * Close the promise. Any errors after this completes will be thrown to the global handler. * * @param {?function(this:void, T, ?)=} onSuccess a function to handle successful * resolution of this promise * @param {?function(this:void, !Error, ?)=} onFailure a function to handle failed * resolution of this promise * @return {void} */ Promise.prototype.done = function (onSuccess, onFailure) { var self = this if (onSuccess || onFailure) { self = self.then(onSuccess, onFailure) } self._end() } /** * Return a new promise that behaves the same as the current promise except * that it will be rejected if the current promise does not get fulfilled * after a certain amount of time. * * @param {number} timeoutMs The timeout threshold in msec * @param {string=} timeoutMsg error message * @return {!Promise.} a new promise with timeout */ Promise.prototype.timeout = function (timeoutMs, timeoutMsg) { var deferred = new Promise() var isTimeout = false var timeout = setTimeout(function() { deferred.reject(new Error(timeoutMsg || 'Promise timeout after ' + timeoutMs + ' ms.')) isTimeout = true }, timeoutMs) this.then(function (data) { if (!isTimeout) { clearTimeout(timeout) deferred.resolve(data) } }, function (err) { if (!isTimeout) { clearTimeout(timeout) deferred.reject(err) } }) return deferred.promise } /** * Attempt to resolve this promise with the specified input * * @param {*} data the input */ Promise.prototype._withInput = function (data) { if (this._successFn) { this._nextTick(this._successFn, [data, this._currentContext]) } else { this.resolve(data) } // context is no longer needed delete this._currentContext } /** * Attempt to reject this promise with the specified error * * @param {!Error} e * @private */ Promise.prototype._withError = function (e) { if (this._failFn) { this._nextTick(this._failFn, [e, this._currentContext]) } else { this.reject(e) } // context is no longer needed delete this._currentContext } /** * Calls a function in the correct scope, and includes bound arguments. * @param {Function} fn * @param {Array} args * @private */ Promise.prototype._nextTick = function (fn, args) { if (this._boundArgs) { args = this._boundArgs.concat(args) } nextTick(nextTickCallback.bind(null, this, fn, this._scope, args)) } /** * Chain a promise to the current promise * * @param {!Promise} promise the promise to chain * @private */ Promise.prototype._chainPromise = function (promise) { var i if (this._hasContext) promise._useContext(this._nextContext) if (this._child) { this._child._chainPromise(promise) } else if (this._hasData) { promise._withInput(this._data) } else if (this._error) { // We can't rely on _withError() because it's called on the chained promises // and we need to use the source's _errorHandled state this._handleError() promise._withError(this._error) } else if (!this._promises) { this._promises = [promise] } else { this._promises.push(promise) } } /** * Utility function used for creating a node-style resolver * for deferreds * * @param {!Promise} deferred a promise that looks like a deferred * @param {Error=} err an optional error * @param {*=} data optional data */ function resolver(deferred, err, data) { if (err) deferred.reject(err) else deferred.resolve(data) } /** * Creates a node-style resolver for a deferred by wrapping * resolver() * * @return {function(?Error, *)} node-style callback */ Promise.prototype.makeNodeResolver = function () { return resolver.bind(null, this) } /** * Return true iff the given object is a promise of this library. * * Because kew's API is slightly different than other promise libraries, * it's important that we have a test for its promise type. If you want * to test for a more general A+ promise, you should do a cap test for * the features you want. * * @param {*} obj The object to test * @return {boolean} Whether the object is a promise */ function isPromise(obj) { return !!obj._isPromise } /** * Return true iff the given object is a promise-like object, e.g. appears to * implement Promises/A+ specification * * @param {*} obj The object to test * @return {boolean} Whether the object is a promise-like object */ function isPromiseLike(obj) { return (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function' } /** * Static function which creates and resolves a promise immediately * * @param {T} data data to resolve the promise with * @return {!Promise.} * @template T */ function resolve(data) { var promise = new Promise() promise.resolve(data) return promise } /** * Static function which creates and rejects a promise immediately * * @param {!Error} e error to reject the promise with * @return {!Promise} */ function reject(e) { var promise = new Promise() promise.reject(e) return promise } /** * Replace an element in an array with a new value. Used by .all() to * call from .then() * * @param {!Array} arr * @param {number} idx * @param {*} val * @return {*} the val that's being injected into the array */ function replaceEl(arr, idx, val) { arr[idx] = val return val } /** * Replace an element in an array as it is resolved with its value. * Used by .allSettled(). * * @param {!Array} arr * @param {number} idx * @param {*} value The value from a resolved promise. * @return {*} the data that's being passed in */ function replaceElFulfilled(arr, idx, value) { arr[idx] = { state: 'fulfilled', value: value } return value } /** * Replace an element in an array as it is rejected with the reason. * Used by .allSettled(). * * @param {!Array} arr * @param {number} idx * @param {*} reason The reason why the original promise is rejected * @return {*} the data that's being passed in */ function replaceElRejected(arr, idx, reason) { arr[idx] = { state: 'rejected', reason: reason } return reason } /** * Takes in an array of promises or literals and returns a promise which returns * an array of values when all have resolved. If any fail, the promise fails. * * @param {!Array.} promises * @return {!Promise.} */ function all(promises) { if (arguments.length != 1 || !Array.isArray(promises)) { promises = Array.prototype.slice.call(arguments, 0) } return allInternal(promises) } /** * A version of all() that does not accept var_args * * @param {!Array.} promises * @return {!Promise.} */ function allInternal(promises) { if (!promises.length) return resolve([]) var outputs = [] var finished = false var promise = new Promise() var counter = promises.length for (var i = 0; i < promises.length; i += 1) { if (!promises[i] || !isPromiseLike(promises[i])) { outputs[i] = promises[i] counter -= 1 } else { promises[i].then(replaceEl.bind(null, outputs, i)) .then(function decrementAllCounter() { counter-- if (!finished && counter === 0) { finished = true promise.resolve(outputs) } }, function onAllError(e) { if (!finished) { finished = true promise.reject(e) } }) } } if (counter === 0 && !finished) { finished = true promise.resolve(outputs) } return promise } /** * Takes in an array of promises or values and returns a promise that is * fulfilled with an array of state objects when all have resolved or * rejected. If a promise is resolved, its corresponding state object is * {state: 'fulfilled', value: Object}; whereas if a promise is rejected, its * corresponding state object is {state: 'rejected', reason: Object}. * * @param {!Array} promises or values * @return {!Promise.} Promise fulfilled with state objects for each input */ function allSettled(promises) { if (!Array.isArray(promises)) { throw Error('The input to "allSettled()" should be an array of Promise or values') } if (!promises.length) return resolve([]) var outputs = [] var promise = new Promise() var counter = promises.length for (var i = 0; i < promises.length; i += 1) { if (!promises[i] || !isPromiseLike(promises[i])) { replaceElFulfilled(outputs, i, promises[i]) if ((--counter) === 0) promise.resolve(outputs) } else { promises[i] .then(replaceElFulfilled.bind(null, outputs, i), replaceElRejected.bind(null, outputs, i)) .then(function () { if ((--counter) === 0) promise.resolve(outputs) }) } } return promise } /** * Takes an array of results and spreads them to the arguments of a function. * @param {!Array} array * @param {!Function} fn */ function spread(array, fn) { resolve(array).spread(fn) } /** * Create a new Promise which looks like a deferred * * @return {!Promise} */ function defer() { return new Promise() } /** * Return a promise which will wait a specified number of ms to resolve * * @param {*} delayMsOrVal A delay (in ms) if this takes one argument, or ther * return value if it takes two. * @param {number=} opt_delayMs * @return {!Promise} */ function delay(delayMsOrVal, opt_delayMs) { var returnVal = undefined var delayMs = delayMsOrVal if (typeof opt_delayMs != 'undefined') { delayMs = opt_delayMs returnVal = delayMsOrVal } if (typeof delayMs != 'number') { throw new Error('Bad delay value ' + delayMs) } var defer = new Promise() setTimeout(function onDelay() { defer.resolve(returnVal) }, delayMs) return defer } /** * Returns a promise that has the same result as `this`, but fulfilled * after at least ms milliseconds * @param {number} ms */ Promise.prototype.delay = function (ms) { return this.then(function (val) { return delay(val, ms) }) } /** * Return a promise which will evaluate the function fn in a future turn with * the provided args * * @param {function(...)} fn * @param {...*} var_args a variable number of arguments * @return {!Promise} */ function fcall(fn, var_args) { var rootArgs = Array.prototype.slice.call(arguments, 1) var defer = new Promise() nextTick(nextTickCallback.bind(null, defer, fn, undefined, rootArgs)) return defer } /** * Returns a promise that will be invoked with the result of a node style * callback. All args to fn should be given except for the final callback arg * * @param {function(...)} fn * @param {...*} var_args a variable number of arguments * @return {!Promise} */ function nfcall(fn, var_args) { // Insert an undefined argument for scope and let bindPromise() do the work. var args = Array.prototype.slice.call(arguments, 0) args.splice(1, 0, undefined) return ncall.apply(undefined, args) } /** * Like `nfcall`, but permits passing a `this` context for the call. * * @param {function(...)} fn * @param {Object} scope * @param {...*} var_args * @return {!Promise} */ function ncall(fn, scope, var_args) { return bindPromise.apply(null, arguments)() } /** * Binds a function to a scope with an optional number of curried arguments. Attaches * a node style callback as the last argument and returns a promise * * @param {function(...)} fn * @param {Object} scope * @param {...*} var_args a variable number of arguments * @return {function(...)}: !Promise} */ function bindPromise(fn, scope, var_args) { var rootArgs = Array.prototype.slice.call(arguments, 2) return function onBoundPromise(var_args) { var defer = new Promise() try { fn.apply(scope, rootArgs.concat(Array.prototype.slice.call(arguments, 0), defer.makeNodeResolver())) } catch (e) { defer.reject(e) } return defer } } module.exports = { all: all, bindPromise: bindPromise, defer: defer, delay: delay, fcall: fcall, isPromise: isPromise, isPromiseLike: isPromiseLike, ncall: ncall, nfcall: nfcall, resolve: resolve, reject: reject, spread: spread, stats: stats, allSettled: allSettled, Promise: Promise, getNextTickFunction: getNextTickFunction, setNextTickFunction: setNextTickFunction, } kew-0.7.0/package.json000066400000000000000000000013471256671041400146050ustar00rootroot00000000000000{ "name": "kew", "description": "a lightweight promise library for node", "version": "0.7.0", "homepage": "https://github.com/Medium/kew", "authors": [ "Jeremy Stanley (https://github.com/azulus)", "Nick Santos ", "Xiao Ma " ], "license": "Apache-2.0", "contributors": [], "keywords": [ "kew", "promises" ], "main": "./kew.js", "repository": { "type": "git", "url": "https://github.com/Medium/kew.git" }, "dependencies": {}, "devDependencies": { "q": "0.9.7", "nodeunit": "0.9.0", "closure-npc": "0.1.5" }, "scripts": { "test": "nodeunit test && closure-npc ./test/closure_test.js --jscomp_error=checkTypes" } } kew-0.7.0/test/000077500000000000000000000000001256671041400132715ustar00rootroot00000000000000kew-0.7.0/test/chain.js000066400000000000000000000254411256671041400147170ustar00rootroot00000000000000var Q = require('../kew') var originalQ = require('q') // test that fin() works with a synchronous resolve exports.testSynchronousThenAndFin = function (test) { var vals = ['a', 'b'] var counter = 0 var promise1 = Q.resolve(vals[0]) var promise2 = promise1.fin(function () { counter++ }) var promise3 = promise2.then(function (data) { if (data === vals[0]) return vals[1] }) var promise4 = promise3.fin(function () { counter++ }) Q.all([promise2, promise4]) .then(function (data) { test.equal(counter, 2, "fin() should have been called twice") test.equal(data[0], vals[0], "first fin() should return the first val") test.equal(data[1], vals[1], "second fin() should return the second val") test.done() }) } // test that fin() works with a synchronous reject exports.testSynchronousFailAndFin = function (test) { var errs = [] errs.push(new Error('nope 1')) errs.push(new Error('nope 2')) var counter = 0 var promise1 = Q.reject(errs[0]) var promise2 = promise1.fin(function () { counter++ }) var promise3 = promise2.fail(function (e) { if (e === errs[0]) throw errs[1] }) var promise4 = promise3.fin(function () { counter++ }) Q.all([ promise2.fail(function (e) { return e === errs[0] }), promise4.fail(function (e) { return e === errs[1] }) ]) .then(function (data) { test.equal(counter, 2, "fin() should have been called twice") test.equal(data[0] && data[1], true, "all promises should return true") test.done() }) } // test that fin() works with an asynchrnous resolve exports.testAsynchronousThenAndFin = function (test) { var vals = ['a', 'b'] var counter = 0 var defer = Q.defer() setTimeout(function () { defer.resolve(vals[0]) }) var promise1 = defer.promise var promise2 = promise1.fin(function () { counter++ }) var promise3 = promise2.then(function (data) { if (data !== vals[0]) return var defer = Q.defer() setTimeout(function () { defer.resolve(vals[1]) }) return defer.promise }) var promise4 = promise3.fin(function () { counter++ }) Q.all([promise2, promise4]) .then(function (data) { test.equal(counter, 2, "fin() should have been called twice") test.equal(data[0], vals[0], "first fin() should return the first val") test.equal(data[1], vals[1], "second fin() should return the second val") test.done() }) } // test that fin() works with an asynchronous reject exports.testAsynchronousFailAndFin = function (test) { var errs = [] errs.push(new Error('nope 1')) errs.push(new Error('nope 2')) var counter = 0 var defer = Q.defer() setTimeout(function () { defer.reject(errs[0]) }, 10) var promise1 = defer.promise var promise2 = promise1.fin(function () { counter++ }) var promise3 = promise2.fail(function (e) { if (e !== errs[0]) return var defer = Q.defer() setTimeout(function () { defer.reject(errs[1]) }, 10) return defer.promise }) var promise4 = promise3.fin(function () { counter++ }) Q.all([ promise2.fail(function (e) { return e === errs[0] }), promise4.fail(function (e) { return e === errs[1] }) ]) .then(function (data) { test.equal(counter, 2, "fin() should have been called twice") test.equal(data[0] && data[1], true, "all promises should return true") test.done() }) } // test several thens chaining exports.testChainedThens = function (test) { var promise1 = Q.resolve('a') var promise2 = promise1.then(function(data) { return data + 'b' }) var promise3 = promise2.then(function (data) { return data + 'c' }) // testing the same promise again to make sure they can run side by side var promise4 = promise2.then(function (data) { return data + 'c' }) Q.all([promise1, promise2, promise3, promise4]) .then(function (data) { test.equal(data[0], 'a') test.equal(data[1], 'ab') test.equal(data[2], 'abc') test.equal(data[3], 'abc') test.done() }) } // test several fails chaining exports.testChainedFails = function (test) { var errs = [] errs.push(new Error("first err")) errs.push(new Error("second err")) errs.push(new Error("third err")) var promise1 = Q.reject(errs[0]) var promise2 = promise1.fail(function (e) { if (e === errs[0]) throw errs[1] }) var promise3 = promise2.fail(function (e) { if (e === errs[1]) throw errs[2] }) var promise4 = promise2.fail(function (e) { if (e === errs[1]) throw errs[2] }) Q.all([ promise1.fail(function (e) { return e === errs[0] }), promise2.fail(function (e) { return e === errs[1] }), promise3.fail(function (e) { return e === errs[2] }), promise4.fail(function (e) { return e === errs[2] }) ]) .then(function (data) { test.equal(data[0] && data[1] && data[2] && data[3], true) test.done() }) } // test that we can call end without callbacks and not fail exports.testEndNoCallbacks = function (test) { Q.resolve(true).end() test.ok("Ended successfully") test.done() } // test that we can call end with callbacks and fail exports.testEndNoCallbacksThrows = function (test) { var testError = new Error('Testing') try { Q.reject(testError).end() test.fail("Should throw an error") } catch (e) { test.equal(e, testError, "Should throw the correct error") } test.done() } // test chaining when a promise returns a promise exports.testChainedPromises = function (test) { var err = new Error('nope') var val = 'ok' var shouldFail = Q.reject(err) var shouldSucceed = Q.resolve(val) Q.resolve("start") .then(function () { return shouldFail }) .fail(function (e) { if (e === err) return shouldSucceed else throw e }) .then(function (data) { test.equal(data, val, "val should be returned") test.done() }) } // test .end() is called with no parent scope (causing an uncaught exception) exports.testChainedEndUncaught = function (test) { var uncaughtErrors = 0 var errs = [] errs.push(new Error('nope 1')) errs.push(new Error('nope 2')) errs.push(new Error('nope 3')) var cb = function (e) { uncaughtErrors++ if (e === errs[2]) { test.equal(uncaughtErrors, 3, "Errors should be uncaught") process.removeListener('uncaughtException', cb) test.done() } } process.on('uncaughtException', cb) var defer = Q.defer() defer.promise.end() var promise1 = defer.promise var promise2 = promise1.fail(function (e) { if (e === errs[0]) throw errs[1] }) var promise3 = promise2.fail(function (e) { if (e === errs[1]) throw errs[2] }) promise1.end() promise2.end() promise3.end() setTimeout(function () { defer.reject(errs[0]) }, 10) } // test .end() is called with a parent scope and is caught exports.testChainedCaught = function (test) { var err = new Error('nope') try { Q.reject(err).end() } catch (e) { test.equal(e, err, "Error should be caught") test.done() } } // test a mix of fails and thens exports.testChainedMixed = function (test) { var errs = [] errs.push(new Error('nope 1')) errs.push(new Error('nope 2')) errs.push(new Error('nope 3')) var vals = [3, 2, 1] var promise1 = Q.reject(errs[0]) var promise2 = promise1.fail(function (e) { if (e === errs[0]) return vals[0] }) var promise3 = promise2.then(function (data) { if (data === vals[0]) throw errs[1] }) var promise4 = promise3.fail(function (e) { if (e === errs[1]) return vals[1] }) var promise5 = promise4.then(function (data) { if (data === vals[1]) throw errs[2] }) var promise6 = promise5.fail(function (e) { if (e === errs[2]) return vals[2] }) Q.all([ promise1.fail(function (e) { return e === errs[0] }), promise2.then(function (data) { return data === vals[0] }), promise3.fail(function (e) { return e === errs[1] }), promise4.then(function (data) { return data === vals[1] }), promise5.fail(function (e) { return e === errs[2] }), promise6.then(function (data) { return data === vals[2] }) ]) .then(function (data) { test.equal(data[0] && data[1] && data[2] && data[3] && data[4] && data[5], true, "All values should return true") test.done() }) } exports.testInteroperabilityWithOtherPromises = function(test) { var promise1 = Q.defer() promise1.then(function(value) { return originalQ(1 + value) }).then(function(result) { test.equal(result, 11) }) var promise2 = Q.defer(), errToThrow = new Error('error') promise2.then(function() { return originalQ.reject(errToThrow) }).fail(function(err) { test.equal(err, errToThrow) }) promise1.resolve(10) promise2.resolve() Q.all([promise1, promise2]).then(function() { test.done() }) } exports.testAllSettled = function(test) { var promise1 = Q.resolve('woot') var promise2 = Q.reject(new Error('oops')) Q.allSettled([promise1, promise2, 'just a string']) .then(function (data) { test.equals('fulfilled', data[0].state) test.equals('woot', data[0].value) test.equals('rejected', data[1].state) test.equals('oops', data[1].reason.message) test.equals('fulfilled', data[2].state) test.equals('just a string', data[2].value) }) Q.allSettled([]) .then(function (data) { test.equals(0, data.length) test.done() }) } exports.testTimeout = function(test) { var promise = Q.delay(50).timeout(45, 'Timeout message') promise.then(function () { test.fail('The promise is supposed to be timeout') }) .fail(function (e) { test.equals('Timeout message', e.message, 'The error message should be the one passed into timeout()') }) .fin(test.done) } exports.testNotTimeout = function(test) { var promise = Q.delay('expected data', 40).timeout(45, 'Timeout message') promise.then(function (data) { test.equals('expected data', data, 'The data should be the data from the original promise') }) .fail(function (e) { test.fail('The promise is supposed to be resolved before the timeout') }) .fin(test.done) } exports.testNotTimeoutButReject = function(test) { var promise = Q.delay(40).then(function() {throw new Error('Reject message')}).timeout(45, 'Timeout message') promise.then(function (data) { test.fail('The promise is supposed to be rejected') }) .fail(function (e) { test.equals('Reject message', e.message, 'The error message should be from the original promise') }) .fin(test.done) } exports.testDelay = function (test) { var timePassed = false setTimeout(function () { timePassed = true }, 10) Q.resolve('expected').delay(20).then(function (result) { test.equal('expected', result) test.ok(timePassed) test.done() }) } kew-0.7.0/test/closure_test.js000066400000000000000000000060621256671041400163460ustar00rootroot00000000000000/** * @fileoverview A sample file to test type-checking */ var kew = require('../kew') var Promise = kew.Promise var all = kew.all var allSettled = kew.allSettled var fcall = kew.fcall var nfcall = kew.nfcall var bindPromise = kew.bindPromise /** @param {Array} result */ var callback = function (result) {}; /** @param {Array} result @param {Array} context */ var callbackWithContext = function (result, context) {}; /** * @param {number} n * @param {*} result */ var callbackNeedsBind = function (n, result) {}; /** @param {Error} error */ var errorCallback = function (error) {}; /** @param {Error} error @param {Array} context */ var errorCallbackWithContext = function (error, context) {}; /** @return {kew.Promise.} */ var stringPromise = function () { return kew.resolve('string') } var exampleThen = function () { var examplePromise = new Promise(); examplePromise.then(callback); examplePromise.setContext([]); examplePromise.then(callbackWithContext); examplePromise.then(null, errorCallback); examplePromise.then(null, errorCallbackWithContext); }; var thenBound = function () { stringPromise().thenBound(callbackNeedsBind, null, 3).failBound(callbackNeedsBind, null, 3); }; var examplePromise = function () { var promise = new Promise(callback); promise = new Promise(callbackWithContext); promise = new Promise(null, errorCallback); promise = new Promise(null, errorCallbackWithContext); }; var exampleFail = function () { var promise = new Promise(); promise.fail(errorCallback); promise.fail(errorCallbackWithContext); }; var exampleResolver = function () { var promise = new Promise(); var resolver = promise.makeNodeResolver(); // success resolver(null, {}); // failure resolver(new Error(), null); }; var exampleAll = function () { // should not compile, but does all([5]); all([{}]); all([null]); all([new Promise(), {}]); all([new Promise(), null]); // good var promise = all([]); all([new Promise(), new Promise()]); }; var exampleAllSettled = function () { allSettled([]); allSettled([5, {}, null, 'string']); var promise = allSettled([new Promise()]); promise.then(function(results){}); }; var exampleTimeout = function () { var promise = new Promise(); var timeoutPromise = promise.timeout(50); timeoutPromise.then(function(result){}); }; var noArgsFunction = function () {}; var exampleFcall = function () { fcall(noArgsFunction); fcall(callback, []); fcall(callbackWithContext, [], 5); }; /** @param {function(Error, *)} nodeCallback */ var noArgsWithNodeCallback = function (nodeCallback) {}; /** @param {!Array} argument @param {function(Error, *)} nodeCallback */ var oneArgWithNodeCallback = function (argument, nodeCallback) {}; var exampleNfcall = function () { var promise = nfcall(noArgsWithNodeCallback); promise = nfcall(oneArgWithNodeCallback, []); }; var exampleBindPromise = function () { callback = bindPromise(noArgsWithNodeCallback, null); callback = bindPromise(noArgsWithNodeCallback, {}); callback = bindPromise(oneArgWithNodeCallback, null, []); }; kew-0.7.0/test/context.js000066400000000000000000000047601256671041400153220ustar00rootroot00000000000000var Q = require('../kew') // test that contexts are propogated based on position exports.testContextWithDelay = function (test) { Q.resolve(true) .setContext({id: 1}) .then(function (val, context) { test.equal(context.id, 1, 'Should return the first context') return Q.delay(500) }) .setContext({id: 2}) .then(function (val, context) { test.equal(context.id, 2, 'Should return the second context') return Q.delay(500) }) .clearContext() .then(function (val, context) { test.equal(typeof context, 'undefined', 'Should return an undefined context') return Q.delay(500) }) .setContext({id: 3}) .fin(test.done) } // test adding and removing contexts exports.testGeneralContextFlow = function (test) { Q.resolve(true) // test no context exists .then(function (val, context) { test.equal(typeof context, 'undefined', 'Context should be undefined') throw new Error() }) .fail(function (e, context) { test.equal(typeof context, 'undefined', 'Context should be undefined') }) // set the context and mutate it .setContext({counter: 1}) .then(function (val, context) { test.equal(context.counter, 1, 'Counter should be 1') context.counter++ }) .then(function (val, context) { test.equal(context.counter, 2, 'Counter should be 2') context.counter++ throw new Error() }) .fail(function (e, context) { test.equal(context.counter, 3, 'Counter should be 3') }) // return a context .then(function (val, context) { return Q.resolve(false) .setContext({counter: 0}) }) .then(function (val, context) { test.equal(context.counter, 0, 'Counter should be 0') throw new Error() }) .fail(function (e, context) { test.equal(context.counter, 0, 'Counter should be 0') }) // returning a promise with a cleared context won't clear the parent context .then(function (val, context) { return Q.resolve(false).clearContext() }) .then(function (val, context) { test.equal(context.counter, 0, 'Counter should be 0') throw new Error() }) .fail(function (e, context) { test.equal(context.counter, 0, 'Counter should be 0') }) // test that clearing the context works .clearContext() .then(function (val, context) { test.equal(typeof context, 'undefined', 'Context should be undefined') throw new Error() }) .fail(function (e, context) { test.equal(typeof context, 'undefined', 'Context should be undefined') }) .fin(test.done) }kew-0.7.0/test/defer.js000066400000000000000000000051641256671041400147220ustar00rootroot00000000000000var Q = require('../kew') // create a deferred which returns a promise exports.testDeferredResolve = function (test) { var val = "ok" var defer = Q.defer() defer.promise .then(function (data) { test.equal(data, val, "Promise successfully returned") test.done() }) setTimeout(function () { defer.resolve(val) }, 50) } // make sure a deferred can only resolve once exports.testDeferredResolveOnce = function (test) { var defer = Q.defer() try { defer.resolve(true) defer.resolve(true) test.fail("Unable to resolve the same deferred twice") } catch (e) { } test.done() } // create a deferred which returns a failed promise exports.testDeferredReject = function (test) { var err = new Error("hello") var defer = Q.defer() defer.promise .fail(function (e) { test.equal(e, err, "Promise successfully failed") test.done() }) setTimeout(function () { defer.reject(err) }, 50) } // make sure a deferred can only reject once exports.testDeferredRejectOnce = function (test) { var defer = Q.defer() try { defer.reject(new Error("nope 1")) defer.reject(new Error("nope 2")) test.fail("Unable to reject the same deferred twice") } catch (e) { } test.done() } // make sure a deferred can only reject once exports.testDeferAndRejectFail = function (test) { var defer try { defer = Q.defer() defer.reject(new Error("nope 1")) defer.resolve(true) test.fail("Unable to reject and resolve the same deferred") } catch (e) { test.ok(true, "Unable to reject and resolve same deferred") } try { defer = Q.defer() defer.resolve(true) defer.reject(new Error("nope 1")) test.fail("Unable to reject and resolve the same deferred") } catch (e) { test.ok(true, "Unable to reject and resolve same deferred") } test.done() } // create a deferred which resolves with a node-standard callback exports.testDeferredResolverSuccess = function (test) { var val = "ok" var defer = Q.defer() var callback = defer.makeNodeResolver() defer.promise .then(function (data) { test.equal(data, val, "Promise successfully returned") test.done() }) setTimeout(function () { callback(null, val) }, 50) } // create a deferred which rejects with a node-standard callback exports.testDeferredResolverSuccess = function (test) { var err = new Error("hello") var defer = Q.defer() var callback = defer.makeNodeResolver() defer.promise .fail(function (e) { test.equal(e, err, "Promise successfully failed") test.done() }) setTimeout(function () { callback(err) }, 50) }kew-0.7.0/test/externs_node.js000066400000000000000000000003221256671041400163210ustar00rootroot00000000000000/* Node externs for Closure Compiler (just enough for kew.js). */ /** @const */ var module = {}; /** @const */ var process = {}; /** @param {function()} callback */ process.nextTick = function (callback) {}; kew-0.7.0/test/later.js000066400000000000000000000016561256671041400147460ustar00rootroot00000000000000var Q = require('../kew') function synchronous (callback) { callback() } var asynchronous = Q.getNextTickFunction() exports.testAsynchronousSynchronous = function (test) { Q.setNextTickFunction(synchronous) var number = 5 Q.resolve(true).then(function () { number = 6 }) test.equals(number, 6, 'Q should resolve synchronously') Q.setNextTickFunction(asynchronous) Q.resolve(true).then(function () { number = 7 }) test.equals(number, 6, 'Q should resolve asynchronously') test.done() } exports.testSetImmediate = function (test) { if (typeof setImmediate == 'undefined') { test.done() return } Q.setNextTickFunction(setImmediate) var number = 5 Q.resolve(true).then(function () { number = 6 }) test.equals(number, 5, 'Q should resolve asynchronously') setImmediate(function () { test.equals(number, 6, 'Q should schedule _successFn synchronously') test.done() }) } kew-0.7.0/test/scopes.js000066400000000000000000000030461256671041400151260ustar00rootroot00000000000000var Q = require('../kew') exports.testThen = function (test) { var detectedScope = null Q.resolve(true).then(function () { detectedScope = this }).then(function () { test.ok(Q.isPromise(detectedScope), 'then() should be called in context of promise') test.done() }) } exports.testFail = function (test) { var detectedScope = null Q.reject(new Error()).fail(function () { detectedScope = this }).then(function () { test.ok(Q.isPromise(detectedScope), 'fail() should be called in context of promise') test.done() }) } exports.testThenBound = function (test) { var detectedScope = scope var scope = {} Q.resolve(true).thenBound(function () { detectedScope = scope }, scope).then(function () { test.ok(detectedScope === scope, 'thenScoped() should be called in context of scope') test.done() }) } exports.testFailBound = function (test) { var detectedScope = scope var scope = {} Q.reject(new Error()).failBound(function () { detectedScope = scope }, scope).then(function () { test.equal(detectedScope, scope, 'failBound() should be called in context of scope') test.done() }) } exports.testThenBoundWithArgs = function (test) { var detectedScope = scope var scope = {} Q.resolve(-1).thenBound(function (a, b, c, d) { test.equal(a, 1) test.equal(b, 2) test.equal(c, 3) test.equal(d, -1) detectedScope = scope }, scope, 1, 2, 3).then(function () { test.ok(detectedScope === scope, 'failScoped() should be called in context of scope') test.done() }) } kew-0.7.0/test/spread.js000066400000000000000000000024771256671041400151170ustar00rootroot00000000000000var Q = require('../kew') exports.testSpreadStatic = function (test) { Q.spread([Q.resolve('a'), 'b'], function (a, b) { test.equal('a', a) test.equal('b', b) test.done() }) } exports.testSpreadMethod = function (test) { Q.resolve(true) .then(function () { return ['a', 'b'] }) .spread(function (a, b) { test.equal('a', a) test.equal('b', b) test.done() }) } exports.testSpreadBoundMethod = function (test) { Q.resolve(true) .then(function () { return [Q.resolve('a'), 'b'] }) .spreadBound(function (c, a, b) { test.equal('scope', this.scope) test.equal('c', c) test.equal('a', a) test.equal('b', b) test.done() }, {scope: 'scope'}, 'c') } exports.testAllSynchronization1 = function (test) { var order = [] Q.resolve(true) .then(function () { var promiseA = Q.fcall(function () { order.push('a') }) var promiseB = Q.fcall(function () { order.push('b') }) test.deepEqual([], order) var promiseAB = Q.all([promiseA, promiseB]) test.deepEqual([], order) return [promiseA, promiseB] }) .then(function (results) { test.deepEqual(['a', 'b'], order) test.done() }) } kew-0.7.0/test/static.js000066400000000000000000000240201256671041400151140ustar00rootroot00000000000000var Q = require('../kew') var originalQ = require('q') // create a promise from a literal exports.testQResolve = function (test) { var val = "ok" Q.resolve(val) .then(function (data) { test.equal(data, val, "Promise successfully returned") test.done() }) } // create a failed promise from an error literal exports.testQReject = function (test) { var err = new Error("hello") Q.reject(err) .fail(function (e) { test.equal(e, err, "Promise successfully failed") test.done() }) } // Test Q.stats exports.testQStatistics = function (test) { var err = new Error("hello") var errorsEmitted = Q.stats.errorsEmitted var errorsHandled = Q.stats.errorsHandled var rejected = Q.reject(err) test.equal(errorsEmitted + 1, Q.stats.errorsEmitted, "One additional error emitted") test.equal(errorsHandled, Q.stats.errorsHandled, "Error hasn't been handled yet") rejected.fail(function (e) { test.equal(e, err, "Promise successfully failed") test.equal(errorsEmitted + 1, Q.stats.errorsEmitted, "One additional error emitted") test.equal(errorsHandled + 1, Q.stats.errorsHandled, "One additional error handled") }) rejected.fail(function (e) { test.equal(e, err, "Promise successfully failed") test.equal(errorsEmitted + 1, Q.stats.errorsEmitted, "One additional error emitted") test.equal(errorsHandled + 1, Q.stats.errorsHandled, "Only count error handling once") }) test.done() } exports.testQDeferredStatistics = function (test) { var err = new Error("hello") var errorsEmitted = Q.stats.errorsEmitted var errorsHandled = Q.stats.errorsHandled var deferred = Q.defer() deferred.fail(function (e) { test.equal(e, err, "Promise successfully failed") test.equal(errorsEmitted + 1, Q.stats.errorsEmitted, "One additional error emitted") test.equal(errorsHandled + 1, Q.stats.errorsHandled, "One additional error handled") test.done() }) var rejected = deferred.reject(err) } // test Q.all with an empty array exports.testQEmptySuccess = function (test) { var promises = [] // make sure all results come back Q.all(promises) .then(function (data) { test.equal(data.length, 0, "No records should be returned") test.done() }) } // test Q.all with only literals exports.testQAllLiteralsSuccess = function (test) { var vals = [3, 2, 1] var promises = [] promises.push(vals[0]) promises.push(vals[1]) promises.push(vals[2]) // make sure all results come back Q.all(promises) .then(function (data) { test.equal(data[0], vals[0], "First val should be returned") test.equal(data[1], vals[1], "Second val should be returned") test.equal(data[2], vals[2], "Third val should be returned") test.done() }) } // test Q.all with only promises exports.testQAllPromisesSuccess = function (test) { var vals = [3, 2, 1] var promises = [] promises.push(Q.resolve(vals[0])) promises.push(Q.resolve(vals[1])) promises.push(Q.resolve(vals[2])) // make sure all results come back Q.all(promises) .then(function (data) { test.equal(data[0], vals[0], "First val should be returned") test.equal(data[1], vals[1], "Second val should be returned") test.equal(data[2], vals[2], "Third val should be returned") test.done() }) } // create a promise which waits for other promises exports.testQAllAssortedSuccess = function (test) { var vals = [3, 2, 1] var promises = [] // a promise that returns the value immediately promises.push(Q.resolve(vals[0])) // the value itself promises.push(vals[1]) // a promise which returns in 10ms var defer = Q.defer() promises.push(defer.promise) setTimeout(function () { defer.resolve(vals[2]) }, 10) // make sure all results come back Q.all(promises) .then(function (data) { test.equal(data[0], vals[0], "First val should be returned") test.equal(data[1], vals[1], "Second val should be returned") test.equal(data[2], vals[2], "Third val should be returned") test.done() }) } // test Q.all with a failing promise exports.testQAllError = function (test) { var vals = [3, 2, 1] var err = new Error("hello") var promises = [] promises.push(vals[0]) promises.push(vals[1]) var defer = Q.defer() promises.push(defer.promise) defer.reject(err) // make sure all results come back Q.all(promises) .fail(function (e) { test.equal(e, err) test.done() }) } // test all var_args exports.testAllVarArgs = function (test) { var promises = ['a', 'b'] Q.all.apply(Q, promises) .then(function (results) { test.equal(promises[0], results[0], "First element should be returned") test.equal(promises[1], results[1], "Second element should be returned") test.done() }) } // test all array exports.testAllArray = function (test) { var promises = ['a', 'b'] Q.all(promises) .then(function (results) { test.equal(promises[0], results[0], "First element should be returned") test.equal(promises[1], results[1], "Second element should be returned") test.done() }) } exports.testAllIsPromiseLike = function(test) { var promises = ['a', originalQ('b')] Q.all(promises) .then(function (results) { test.equal(promises[0], 'a', "First element should be returned") test.equal(promises[1], 'b', "Second element should be returned") test.done() }) } // test delay exports.testDelay = function (test) { var val = "Hello, there" var startTime = Date.now() Q.resolve(val) .then(function (v) { return Q.delay(v, 1000) }) .then(function (returnVal) { test.equal(returnVal, val, "Val should be passed through") var diff = Date.now() - startTime // clock granularity may be off by 15 test.equal(diff >= 1000 - 15, true, "Should have waited a second. Actually waited " + diff) test.done() }) } // test fcall exports.testFcall = function (test) { var calledYet = false var adder = function (a, b) { calledYet = true return a + b } Q.fcall(adder, 2, 3) .then(function (val) { test.equal(val, 5, "Val should be 2 + 3") test.done() }) test.ok(!calledYet, "fcall() should delay function invocation until next tick") } // test fcall exports.testFcallError = function (test) { var error = function () { throw new Error('my error') } Q.fcall(error) .then(function (val) { test.fail('fcall should throw exception') }, function (err) { test.equal('my error', err.message) }) .then(function () { test.done() }) } // test fcall works when fn returns a promise exports.testFcallGivenPromise = function (test) { var calledYet = false var eventualAdd = function (a, b) { calledYet = true return Q.resolve(a + b) } Q.fcall(eventualAdd, 2, 3) .then(function (val) { test.equal(val, 5, "Val should be 2 + 3") test.done() }) test.ok(!calledYet, "fcall() should delay function invocation until next tick") } // test nfcall, successful case exports.testNfcall = function (test) { var nodeStyleEventualAdder = function (a, b, callback) { setTimeout(function () { callback(undefined, a + b) }, 2) } Q.nfcall(nodeStyleEventualAdder, 2, 3) .then(function (val) { test.equal(val, 5, "Val should be 2 + 3") test.done() }) } // test nfcall, error case exports.testNfcallErrors = function (test) { var err = new Error('fail') var nodeStyleFailer = function (a, b, callback) { setTimeout(function() { callback(err) }, 2) } Q.nfcall(nodeStyleFailer, 2, 3) .fail(function (e) { test.equal(e, err, "Promise successfully failed") test.done() }) } // test fcall exports.testNFcallErrorSync = function (test) { var error = function () { throw new Error('my error') } Q.nfcall(error) .then(function (val) { test.fail('nfcall should throw exception') }, function (err) { test.equal('my error', err.message) }) .then(function () { test.done() }) } exports.testNcall = function (test) { function TwoAdder() { this.a = 2 } TwoAdder.prototype.add = function (num, callback) { setTimeout(function () { callback(null, this.a + num) }.bind(this), 10) } var adder = new TwoAdder() Q.ncall(adder.add, adder, 3) .then(function (val) { test.equal(val, 5, "Val should be 2 + 3") test.done() }) } // test binding a callback function with a promise exports.testBindPromise = function (test) { var adder = function (a, b, callback) { callback(null, a + b) } var boundAdder = Q.bindPromise(adder, null, 2) boundAdder(3) .then(function (val) { test.equal(val, 5, "Val should be 2 + 3") test.done() }) } // test checking whether something is a promise exports.testIsPromise = function (test) { var kewPromise = Q.defer() var qPromise = originalQ(10) var kewLikeObject = { promise: function () { return 'not a promise sucka!' }, then: function (fn) { fn('like a promise, brah!') } } test.equal(Q.isPromise(kewPromise), true, 'A Kew promise is a promise') test.equal(Q.isPromise(qPromise), false, 'A Q promise is not a promise') test.equal(Q.isPromise(kewLikeObject), false, 'A pretend promise is not a promise') test.done() } // test checking whether something is a promise-like object exports.testIsPromiseLike = function (test) { var kewPromise = Q.defer() var qPromise = originalQ(10) var kewLikeObject = { promise: function () { return 'not a promise sucka!' }, then: function (fn) { fn('like a promise, brah!') } } var kewLikeFunction = function() {} kewLikeFunction.then = function(fn) { fn('like a promise, brah!') } test.equal(Q.isPromiseLike(kewPromise), true, 'A Kew promise is promise-like') test.equal(Q.isPromiseLike(qPromise), true, 'A Q promise is promise-like') test.equal(Q.isPromiseLike(kewLikeObject), true, 'A pretend promise is a promise-like') test.equal(Q.isPromiseLike(kewLikeFunction), true, 'A pretend function promise is a promise-like') test.done() }