function Promise_all(questions) {
    var countDown = 0;
    var deferred = Promise_defer();
    var answers = Array(questions.length);
    var estimates = [];
    var estimate = -Infinity;
    var setEstimate;
    Array.prototype.forEach.call(questions, function Promise_all_each(promise, index) {
        var handler;
        if (
            isPromise(promise) &&
            (handler = Promise_getHandler(promise)).state === "fulfilled"
        ) {
            answers[index] = handler.value;
        } else {
            ++countDown;
            promise = Promise_return(promise);
            promise.done(
                function Promise_all_eachFulfilled(value) {
                    answers[index] = value;
                    if (--countDown === 0) {
                        deferred.resolver.return(answers);
                    }
                },
                deferred.reject
            );

            promise.observeEstimate(function Promise_all_eachEstimate(newEstimate) {
                var oldEstimate = estimates[index];
                estimates[index] = newEstimate;
                if (newEstimate > estimate) {
                    estimate = newEstimate;
                } else if (oldEstimate === estimate && newEstimate <= estimate) {
                    // There is a 1/length chance that we will need to perform
                    // this O(length) walk, so amortized O(1)
                    computeEstimate();
                }
                if (estimates.length === questions.length && estimate !== setEstimate) {
                    deferred.setEstimate(estimate);
                    setEstimate = estimate;
                }
            });

        }
    });

    function computeEstimate() {
        estimate = -Infinity;
        for (var index = 0; index < estimates.length; index++) {
            if (estimates[index] > estimate) {
                estimate = estimates[index];
            }
        }
    }

    if (countDown === 0) {
        deferred.resolver.return(answers);
    }

    return deferred.promise;
}
Turns an array of promises into a promise for an array.  If any of the
promises gets rejected, the whole array is rejected immediately.
@param {Array.<Promise>} an array (or promise for an array) of values (or
promises for values)
@returns {Promise.<Array>} a promise for an array of the corresponding values