|
"use strict"; |
|
|
|
var Q = require("q"); |
|
var dialogs = require("./dialogs"); |
|
var drm = require("./drm"); |
|
var spinner = require("./spinner"); // Implementation not shown; has start() and stop() methods. |
|
|
|
// C++ has nicely tacked this method onto the window object for us. |
|
// Signature: printImpl(bookId, pageNumber, onPrintDone(err)) |
|
// (For simplicity let's say the user can only print one page through our interface.) |
|
var printImpl = window.cPlusPlusApis.print; |
|
|
|
// Promisify the C++ method, which happily follows the Node-style callback-with-error-as-first-argument |
|
// convention. |
|
var printImplAsync = Q.denodeify(printImpl); |
|
|
|
// 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(); |
|
} else { |
|
// 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."); |
|
} else { |
|
// 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."); |
|
} |
|
}); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
// The promise returned here will always be fulfilled, since we transform errors into user notifications. |
|
// But, when the promise is fulfilled, you can be sure that the entire printing process is done, one way |
|
// or another. |
|
exports.printAsync = function (book) { |
|
return ensureEnhancedAsync(book) |
|
.then(function () { |
|
return dialogs.promptAsync("What page would you like to print?").then(function (pageNumber) { |
|
// If they gave us a page number, print it! |
|
if (pageNumber) { |
|
// Start up the spinner so as to block the UI and avoid deadlocks! |
|
spinner.start(); |
|
return printImplAsync(book.id, pageNumber); |
|
} |
|
// If they cancelled out of this prompt, then this method has done its job, and we can |
|
// just let it return a fulfilled promise. |
|
}); |
|
}) |
|
.catch(function (error) { |
|
// If we got an error, either in the ensureEnhancedAsync processing or bubbled up from |
|
// printImplAsync, alert the user. |
|
return dialogs.alertAsync(error.message); |
|
}) |
|
.finally(spinner.stop); // Make sure to stop the spinner on either success or failure |
|
// in order to unblock the UI. |
|
}; |
I think it will look better for promises if it is kept flat like:
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...