function withTimeout(promise, timeout) {
        var wrapper = new $.Deferred();
        
        var timer = window.setTimeout(function () {
            wrapper.reject(ERROR_TIMEOUT);
        }, timeout);
        promise.always(function () {
            window.clearTimeout(timer);
        });
        
        // If the wrapper was already rejected due to timeout, the Promise's calls to resolve/reject
        // won't do anything
        promise.pipe(wrapper.resolve, wrapper.reject);
        
        return wrapper.promise();
    }
Adds timeout-driven failure to a Promise: returns a new Promise that is resolved/rejected when
the given original Promise is resolved/rejected, OR is rejected after the given delay - whichever
happens first.

If the original Promise is resolved/rejected first, done()/fail() handlers receive arguments
piped from the original Promise. If the timeout occurs first instead, fail() is called with the
token Async.ERROR_TIMEOUT.

@param {$.Promise} promise
@param {number} timeout
@return {$.Promise}