docs: function defer(canceler) {
              var deferred, promise, handlers, progressHandlers,
                  _then, _notify, _resolve;
  
              /**
               * The promise for the new deferred
               * @type {Promise}
               */
              promise = new Promise();
              promise.then = then;
              promise.hasHandlers = hasHandlers;
  
              /**
               * The full Deferred object, with {@link Promise} and {@link Resolver} parts
               * @class Deferred
               * @name Deferred
               */
              deferred = {
                  resolve: promiseResolve, reject: promiseReject, notify: promiseNotify, promise: promise,
                  resolver: { resolve: promiseResolve, reject: promiseReject, notify: promiseNotify }
              };
  
              if (canceler) {
                  deferred.cancel = promise.cancel = function (reason) {
                      return deferred.reject(canceler(reason));
                  }
              }
  
              function hasHandlers() {
                  return handlers && handlers.length > 0;
              }
  
              /**
               * Pre-resolution then() that adds the supplied callback, errback, and progback
               * functions to the registered listeners
               * @private
               *
               * @param {function?} [onFulfilled] resolution handler
               * @param {function?} [onRejected] rejection handler
               * @param {function?} [onProgress] progress handler
               */
              _then = function (onFulfilled, onRejected, onProgress) {
                  var d, progressHandler;
  
                  d = defer();
  
                  progressHandler = typeof onProgress === 'function' ?
                      function (update) {
                          try {
                              // Allow progress handler to transform progress event
                              d.notify(onProgress(update));
                          } catch (e) {
                              // Use caught value as progress
                              d.notify(e);
                          }
                      } :
                      function (update) {
                          d.notify(update);
                      };
  
                  if (!handlers) handlers = [];
                  handlers.push(function (promise) {
                      var p = promise.then(onFulfilled, onRejected);
                      // Throw unhandled exception.
                      if (states.isRejected(p) && !d.promise.hasHandlers()) throw p.reason;
                      return p.then(d.resolve, d.reject, progressHandler);
                  });
  
                  if (!progressHandlers) progressHandlers = [];
                  progressHandlers.push(progressHandler);
  
                  return d.promise;
              };
  
              /**
               * Issue a progress event, notifying all progress listeners
               * @private
               * @param {*} update progress event payload to pass to all listeners
               */
              _notify = function (update) {
                  processQueue(progressHandlers, update);
                  return update;
              };
  
              /**
               * Transition from pre-resolution state to post-resolution state, notifying
               * all listeners of the resolution or rejection
               * @private
               * @param {*} value the value of this deferred
               */
              _resolve = function (value) {
                  // Replace _then with one that directly notifies with the result.
                  _then = value.then;
                  // Replace _resolve so that this Deferred can only be resolved once
                  _resolve = resolve;
                  // Make _progress a noop, to disallow progress for the resolved promise.
                  _notify = identity;
  
                  states.resolve(promise, value);
  
                  // Notify handlers
                  processQueue(handlers, value);
  
                  // Free progressHandlers array since we'll never issue progress events
                  progressHandlers = handlers = undef;
  
                  return value;
              };
  
              return deferred;
  
              /**
               * Wrapper to allow _then to be replaced safely
               * @param {function?} [onFulfilled] resolution handler
               * @param {function?} [onRejected] rejection handler
               * @param {function?} [onProgress] progress handler
               * @return {Promise} new promise
               */
              function then(onFulfilled, onRejected, onProgress) {
                  // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress
                  return _then(onFulfilled, onRejected, onProgress);
              }
  
              /**
               * Wrapper to allow _resolve to be replaced
               */
              function promiseResolve(val) {
                  return _resolve(resolve(val));
              }
  
              /**
               * Wrapper to allow _reject to be replaced
               */
              function promiseReject(err) {
                  return _resolve(rejected(err));
              }
  
              /**
               * Wrapper to allow _notify to be replaced
               */
              function promiseNotify(update) {
                  return _notify(update);
              }
          }