Q.js手册

开头

Everything above assumes you get a promise from somewhere else. This is the common case. Every once in a while, you will need to create a promise from scratch.

使用 Q.fcall

You can create a promise from a value using Q.fcall. This returns a promise for 10.

return Q.fcall(function () {
    return 10;
});

You can also use fcall to get a promise for an exception.

return Q.fcall(function () {
    throw new Error("Can't do it");
});

As the name implies, fcall can call functions, or even promised functions. This uses the eventualAdd function above to add two numbers.

return Q.fcall(eventualAdd, 2, 2);

使用延迟

If you have to interface with asynchronous functions that are callback-based instead of promise-based, Q provides a few shortcuts (like Q.nfcall and friends). But much of the time, the solution will be to use deferreds.

var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
    if (error) {
        deferred.reject(new Error(error));
    } else {
        deferred.resolve(text);
    }
});
return deferred.promise;

Note that a deferred can be resolved with a value or a promise. The reject function is a shorthand for resolving with a rejected promise.

// this:
deferred.reject(new Error("Can't do it"));

// is shorthand for:
var rejection = Q.fcall(function () {
    throw new Error("Can't do it");
});
deferred.resolve(rejection);

This is a simplified implementation of Q.delay.

function delay(ms) {
    var deferred = Q.defer();
    setTimeout(deferred.resolve, ms);
    return deferred.promise;
}

This is a simplified implementation of Q.timeout

function timeout(promise, ms) {
    var deferred = Q.defer();
    Q.when(promise, deferred.resolve);
    delay(ms).then(function () {
        deferred.reject(new Error("Timed out"));
    });
    return deferred.promise;
}

Finally, you can send a progress notification to the promise with deferred.notify.

For illustration, this is a wrapper for XML HTTP requests in the browser. Note that a more thorough implementation would be in order in practice.

function requestOkText(url) {
    var request = new XMLHttpRequest();
    var deferred = Q.defer();

    request.open("GET", url, true);
    request.onload = onload;
    request.onerror = onerror;
    request.onprogress = onprogress;
    request.send();

    function onload() {
        if (request.status === 200) {
            deferred.resolve(request.responseText);
        } else {
            deferred.reject(new Error("Status code was " + request.status));
        }
    }

    function onerror() {
        deferred.reject(new Error("Can't XHR " + JSON.stringify(url)));
    }

    function onprogress(event) {
        deferred.notify(event.loaded / event.total);
    }

    return deferred.promise;
}

Below is an example of how to use this requestOkText function:

requestOkText("http://localhost:3000")
.then(function (responseText) {
    // If the HTTP response returns 200 OK, log the response text.
    console.log(responseText);
}, function (error) {
    // If there's an error or a non-200 status code, log the error.
    console.error(error);
}, function (progress) {
    // Log the progress as it comes in.
    console.log("Request progress: " + Math.round(progress * 100) + "%");
});

使用 Q.Promise

This is an alternative promise-creation API that has the same power as the deferred concept, but without introducing another conceptual entity.

Rewriting the requestOkText example above using Q.Promise:

function requestOkText(url) {
    return Q.Promise(function(resolve, reject, notify) {
        var request = new XMLHttpRequest();

        request.open("GET", url, true);
        request.onload = onload;
        request.onerror = onerror;
        request.onprogress = onprogress;
        request.send();

        function onload() {
            if (request.status === 200) {
                resolve(request.responseText);
            } else {
                reject(new Error("Status code was " + request.status));
            }
        }

        function onerror() {
            reject(new Error("Can't XHR " + JSON.stringify(url)));
        }

        function onprogress(event) {
            notify(event.loaded / event.total);
        }
    });
}

If requestOkText were to throw an exception, the returned promise would be rejected with that thrown exception as the rejection reason.