Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active June 24, 2021 16:37
Show Gist options
  • Save domenic/1604916 to your computer and use it in GitHub Desktop.
Save domenic/1604916 to your computer and use it in GitHub Desktop.
Using promises in a UI context

The scenario:

  • We are writing a digital textbook-reading app.
  • You get two copies of each digital textbooks: "basic" or "enhanced." Only one computer can have the enhanced copy.
  • You can only print from the enhanced copy.

The problem statement:

  • We want a simple printAsync() method that we can attach to our click handler.

The complications:

  • If the user has the basic version, we need to check if it's possible for them to upgrade to enhanced:
    • If so, ask the user if they want to upgrade, and then continue the print operation.
    • If not, error out: printing is impossible.
  • Printing is an asynchronous operation, that could fail (e.g. if the printer is out of paper).
  • Due to some horrible threaded C++ code, we get deadlocks if the user navigates through the book at the same time as the book is printing, so we need to block the UI while it prints.
define(function (require, exports, module) {
var Q = require("Q");
var system = require("./system"); // Implementation not shown. It has a computerId export.
// Returns a promise for a boolean.
exports.getCouldUpgradeAsync = function (book) {
var jQueryPromise = $.get("/book/" + book.id + "/drm/status", "json");
var qPromise = Q.when(jQueryPromise);
// pluck the isEnhancedAvailable property off of the JSON result.
return qPromise.get("isEnhancedAvailable");
};
// Returns a promise with no fulfillment value (equivalent to a synchronous function that doesn't return anything).
// The returned promise will be fulfilled if the HTTP POST succeeds, meaning the enhanced license has been acquired
// for this computer.
// It will be broken if the HTTP POST fails (e.g. server sends back 500 error), which we interpret as meaning that
// for some reason we couldn't assign an enhanced license to this computer.
exports.upgradeAsync = function (book) {
var jQueryPromise = $.post("/book/" + book.id + "/drm/acquire-enhanced-license?computerId=" + system.computerId);
var qPromise = Q.when(jQueryPromise);
// Convert a HTTP error into a meaningful error object.
return qPromise.then(
null, // pass through success
function () {
throw new Error("Could not acquire enhanced license for this computer.");
}
);
};
});
@petkaantonov
Copy link

I think it will look better for promises if it is kept flat like:

// Returns a promise that will be fulfilled if and only if the book becomes (or already is) an
// enhanced copy.
function ensureEnhancedAsync(book) {
    if (!book.isEnhanced) {
        // Return an already-fulfilled promise (with no fulfillment value): we're good to go.
        return Q();
    } 
    // First see if we could possibly upgrade.
    return drm.getCouldUpgradeAsync().then(function (couldUpgrade) {
        // Now act on that knowledge: either give up if we can't, or upgrade if we can.
        if( !couldUpgrade ){
            // This will bubble up, resulting in the promise returned from ensureEnhancedAsync
            // being rejected.
            throw new Error("You cannot print from the basic version, and have already downloaded "
                          + "the enhanced version on another computer. Use that computer instead.");             
        }
        // Ask the user if they want to upgrade.
        return dialogs.confirmAsync("Do you want to upgrade to enhanced?")
    }).then( function( result ) {
        // If they do, perform the upgrade.
        if( result ) {
            return drm.upgradeAsync(book);
        }
        else {
            throw new Error("Since you have not chosen to upgrade, printing is not available.");
        }
    });

}

This should work the same if I'm not mistaken. Right now it almost looks like no callback hell was solved after all as the code keeps growing on the right...

Copy link

ghost commented Jun 23, 2015

Awesome demonstration of the power of promises!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment