/ajax/libs//0.1.1/q.js
JavaScript | 361 lines | 192 code | 38 blank | 131 comment | 22 complexity | 46c6098624f03f9a61e7fd43020121b6 MD5 | raw file
- // Tyler Close
- // Ported by Kris Kowal
- // Variation to illustrated ideas for improvements on the API.
- // * Deferred, Rejection, Reference instead of defer, reject, ref, and promise.
- // * Promise constructor that takes a descriptor and fallback.
- // * near has been changed to valueOf, and uses a valueOf operator instead
- // an undefined operator, to reduce special cases.
- // * variadic arguments are used internally where applicable (POST arguments
- // have not yet been altered.
- /*
- * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
- * at http://www.opensource.org/licenses/mit-license.html
- *
- * ref_send.js version: 2009-05-11
- */
- /*whatsupdoc*/
- // - the enclosure ensures that this module will function properly both as a
- // CommonJS module and as a script in the browser. In CommonJS, this module
- // exports the "Q" API. In the browser, this script creates a "Q" object in
- // global scope.
- // - the use of "undefined" on the enclosure is a micro-optmization for
- // compression systems, permitting every occurrence of the "undefined" keyword
- // bo be replaced with a single-character.
- (function (exports, undefined) {
- "use strict";
- // this provides an enqueue method in browsers, Narwhal, and NodeJS
- var enqueue;
- if (typeof setTimeout === "function") {
- enqueue = function (task) {
- setTimeout(task, 0);
- };
- } else {
- enqueue = require("event-loop").enqueue;
- }
- var print;
- if (typeof require !== "undefined") {
- try {
- print = require("system").print; // CommonJS
- } catch (exception) {
- print = require("sys").puts; // Node
- }
- } else if (typeof console !== "undefined") {
- print = function (message) {
- console.log(message);
- };
- } else {
- print = function () {}
- }
- /**
- * Performs a task in a future turn of the event loop.
- * @param {Function} task
- */
- exports.enqueue = enqueue;
- /**
- * Constructs a {promise, resolve} object.
- *
- * The resolver is a callback to invoke with a more resolved value for the
- * promise. To fulfill the promise, invoke the resolver with any value that is
- * not a function. To reject the promise, invoke the resolver with a rejection
- * object. To put the promise in the same state as another promise, invoke the
- * resolver with that other promise.
- */
- exports.defer = defer;
- function defer() {
- // if "pending" is an "Array", that indicates that the promise has not yet
- // been resolved. If it is "undefined", it has been resolved. Each
- // element of the pending array is itself an array of complete arguments to
- // forward to the resolved promise. We coerce the resolution value to a
- // promise using the ref promise because it handles both fully
- // resolved values and other promises gracefully.
- var pending = [], value;
- var promise = Object.create(Promise.prototype);
- promise.emit = function () {
- var args = Array.prototype.slice.call(arguments);
- if (pending) {
- pending.push(args);
- } else {
- forward.apply(undefined, [value].concat(args));
- }
- };
- var resolve = function (resolvedValue) {
- var i, ii, task;
- if (!pending)
- return;
- value = ref(resolvedValue);
- for (i = 0, ii = pending.length; i < ii; ++i) {
- forward.apply(undefined, [value].concat(pending[i]));
- }
- pending = undefined;
- };
- return {
- "promise": promise,
- "resolve": resolve,
- "reject": function (reason) {
- resolve(reject(reason));
- }
- };
- }
- /**
- * Constructs a Promise with a promise descriptor object and optional fallback
- * function. The descriptor contains methods like when(rejected), get(name),
- * put(name, value), post(name, args), delete(name), and valueOf(), which all
- * return either a value, a promise for a value, or a rejection. The fallback
- * accepts the operation name, a resolver, and any further arguments that would
- * have been forwarded to the appropriate method above had a method been
- * provided with the proper name. The API makes no guarantees about the nature
- * of the returned object, apart from that it is usable whereever promises are
- * bought and sold.
- */
- exports.Promise = Promise;
- function Promise(descriptor, fallback) {
- if (fallback === undefined) {
- fallback = function (op) {
- return reject("Promise does not support operation: " + op);
- };
- }
- var promise = Object.create(Promise.prototype);
- promise.emit = function (op, resolved /* ...args */) {
- var args = Array.prototype.slice.call(arguments, 2);
- var result;
- if (descriptor[op])
- result = descriptor[op].apply(descriptor, args);
- else
- result = fallback.apply(descriptor, arguments);
- if (resolved)
- return resolved(result);
- return result;
- };
- return promise;
- };
- Promise.prototype.toSource = function () {
- return this.toString();
- };
- Promise.prototype.toString = function () {
- return '[object Promise]';
- };
- Promise.prototype.valueOf = function () {
- return this.emit("valueOf");
- };
- /**
- * @returns whether the given object is a promise.
- * Otherwise it is a resolved value.
- */
- exports.isPromise = isPromise;
- function isPromise(object) {
- return object instanceof Promise;
- };
- /**
- * Constructs a rejected promise.
- * @param reason value describing the failure
- */
- exports.reject = reject;
- function reject(reason) {
- return Promise({
- "when": function (rejected) {
- return rejected ? rejected(reason) : reject(reason);
- }
- }, function fallback(op, resolved) {
- var rejection = reject(reason);
- return resolved ? resolved(rejection) : rejection;
- });
- }
- /**
- * Constructs a promise for an immediate reference.
- * @param value immediate reference
- */
- exports.ref = ref;
- function ref(object) {
- // If the object is already a Promise, return it directly. This enables
- // the ref function to both be used to created references from
- // objects, but to tolerably coerce non-promises to refs if they are
- // not already Promises.
- if (isPromise(object))
- return object;
- return Promise({
- "when": function (rejected) {
- return object;
- },
- "get": function (name) {
- return object[name];
- },
- "put": function (name, value) {
- object[name] = value;
- },
- "delete": function (name) {
- delete object[name];
- },
- "post": function (name, args) {
- return object[name].apply(object, args);
- },
- "valueOf": function () {
- return object;
- }
- });
- }
- /**
- * Constructs a promise method that can be used to safely observe resolution of
- * a promise for an arbitrarily named method like "propfind" in a future turn.
- *
- * "Method" constructs methods like "get(promise, name)" and "put(promise)".
- */
- exports.Method = Method;
- function Method (methodName) {
- return function (object) {
- var deferred = defer();
- var args = Array.prototype.slice.call(arguments, 1);
- forward.apply(undefined, [
- ref(object),
- methodName,
- deferred.resolve
- ].concat(args));
- return deferred.promise;
- };
- }
- /**
- * Registers an observer on a promise.
- *
- * Guarantees:
- *
- * 1. that resolved and rejected will be called only once.
- * 2. that either the resolved callback or the rejected callback will be
- * called, but not both.
- * 3. that resolved and rejected will not be called in this turn.
- *
- * @param value promise or immediate reference to observe
- * @param resolve function to be called with the resolved value
- * @param rejected function to be called with the rejection reason
- * @return promise for the return value from the invoked callback
- */
- exports.when = function (value, resolved, rejected) {
- var deferred = defer();
- var done = false; // ensure the untrusted promise makes at most a
- // single call to one of the callbacks
- forward(ref(value), "when", function (value) {
- if (done)
- return;
- done = true;
- deferred.resolve(ref(value).emit("when", resolved, rejected));
- }, function (reason) {
- if (done)
- return;
- done = true;
- deferred.resolve(rejected ? rejected(reason) : reject(reason));
- });
- return deferred.promise;
- };
- /**
- */
- exports.asap = function (value, resolved, rejected) {
- if (exports.isPromise(value)) {
- return exports.when(value, resolved, rejected);
- } else {
- return resolved(value);
- }
- };
- /**
- * Gets the value of a property in a future turn.
- * @param object promise or immediate reference for target object
- * @param name name of property to get
- * @return promise for the property value
- */
- exports.get = Method("get");
- /**
- * Sets the value of a property in a future turn.
- * @param object promise or immediate reference for object object
- * @param name name of property to set
- * @param value new value of property
- * @return promise for the return value
- */
- exports.put = Method("put");
- /**
- * Deletes a property in a future turn.
- * @param object promise or immediate reference for target object
- * @param name name of property to delete
- * @return promise for the return value
- */
- exports.del = Method("del");
- /**
- * Invokes a method in a future turn.
- * @param object promise or immediate reference for target object
- * @param name name of method to invoke
- * @param argv array of invocation arguments
- * @return promise for the return value
- */
- exports.post = Method("post");
- /**
- * Guarantees that the give promise resolves to a defined, non-null value.
- */
- exports.defined = function (value) {
- return exports.when(value, function (value) {
- if (value === undefined || value === null)
- return reject("Resolved undefined value: " + value);
- return value;
- });
- };
- /**
- * Throws an error with the given reason.
- */
- exports.error = function (reason) {
- if (!(reason instanceof Error))
- reason = new Error(reason);
- throw reason;
- };
- /*
- * Enqueues a promise operation for a future turn.
- */
- function forward(promise /*, op, resolved, ... */) {
- var args = Array.prototype.slice.call(arguments, 1);
- enqueue(function () {
- try {
- promise.emit.apply(promise, args);
- } catch (exception) {
- print(exception.stack || exception);
- }
- });
- }
- // Complete the closure: use either CommonJS exports or browser global Q object
- // for the exports internally.
- })(
- typeof exports !== "undefined" ?
- exports :
- this["/q"] = {}
- );