I'm going to talk about 'promises' and for once as an Oracle employee not include a safe harbor slide 😉
The February 2016 Oracle Mobile Cloud Service (MCS) release has two handy updates to the server side SDK exposed to custom API service developers in Node.js.
As an existing MCS customer you'd be familiar that within your custom API Node code, you used a generic server side SDK rest call for consuming the other parts of your MCS implementation through a call to the req.oracleMobile object and its methods get, put, post, delete and so on. For example in the following Node example we can see a call to a MCS connector AussieWeatherConn using the generic REST call to get data from an external service and return it to the mobile client:
service.get("/mobile/custom/AussieWeather/Perth", function(req, res) {
var payload = {};
payload.uri = "/mobile/connector/AussieWeatherConn/Perth";
req.oracleMobile.rest.get(payload, function(err1, res1, body1) {
if (err1) {
res.send(res1.statusCode, err1.message);
} else {
res.send(200, body1);
}
});
});
This generic mechanism can be used to call all sorts of APIs inside MCS, including connectors, analytics, the storage API, or even other custom APIs. However as we're treating everything as a REST endpoint, as you can see it's a bit clunky. We have to pass a payload containing the REST URI as a string and other parameters to the req.oracleMobile.rest.get method, there is no direct support for the MCS connector API I'm calling. Couldn't MCS provide dedicated SDK methods to call all the different REST APIs which can protect us from silly programming mistakes?
While this generic SDK mechanism continues to be supported in the February 2016 MCS release, MCS has introduced in the server side SDK methods dedicated methods to all the server side APIs (with the exception of the database API which is planned for a later release). Taking the connector example from above, in the following code you can see the new call supported for connectors:
service.get("/mobile/custom/AussieWeather/Perth", function(req, res) {
req.oracleMobile.connectors.AussieWeatherConn.get("Perth").then(
function(success) {
res.send(200, success.result);
},
function(failure) {
res.send(500, failure.error);
}
);
});
Compared to the previous example first note the SDK call to req.oracleMobile now includes a connectors object (as well as analytics, notifications and more to support the other APIs). Then also note the inline syntax support for referring to the AussieWeatherConn object that previously we had to name in a string.
The other major change you may note is the callback handler via the connector call being replaced by a then() call with separate functions to handle the success or failure. This new syntax is the support for Promises.
Promises originated from functional programming languages to make working with asynchronous functions easier, and have surfaced in many modern languages like Javascript and Node. To date I haven't personally found a simple definition of promises that I think conveys succinctly to beginners what they mean to your programming. For example take the following Wikipedia definition:
Futures and promises originated in functional programming and related paradigms (such as logic programming) to decouple a value (a future) from how it was computed (a promise), allowing the computation to be done more flexibly, notably by parallelizing it.
Huh?
Possibly the better approach to explaining Promises is to show you the change between SDK calls in the different MCS releases.
In the original example the req.oracleMobile.rest.get method accepts as a parameter a callback handler function to support the asynchronous callback when the connector returned a result.
The classic problem with callback handlers is that if you then need to call another connector as a result of the first, and another as result of the second and so on, you end up with a nesting hell, something like the following:
var payload1 = {};
payload1.uri = "/mobile/connector/Conn1/Resource1";
req.oracleMobile.rest.get(payload1, function (err1, res1, body1) {
if (err1) {
// handle error
} else {
// handle success
var payload2 = {};
payload2.uri = "/mobile/connector/Conn2/Resource2";
req.oracleMobile.rest.get(payload2, function(err2, res2, body2) {
if (err2) {
// handle error
} else {
// handle success
var payload3 = {};
payload3.uri = "/mobile/connector/Conn3/Resource3";
req.oracleMobile.rest.get(payload3, function(err3, res3, body3) {
// Argh! Are we done yet?
While the above example is fairly simplistic and nicely condensed for this blog, imagine sprinkling those nested calls with the additional logic you need beyond just the req.oracleMobile calls, to deal with the payloads. It's going to get pretty messy, hard to maintain and just hard to read in general, especially for the next coder who has to understand this nesting logic.
With the introduction of Promises and the February 2016 SDK enhancement we can instead do the following:
req.oracleMobile.connectors.Conn1.get("Resource1").then(
function(success1) {
// handle success
},
function(error1) {
// handle error
}
).then(
function(result2) {
req.oracleMobile.connectors.Conn2.get("Resource2").then(
function(success2) {
// handle success
},
function(error2) {
// handle error
}
}).then(
function(result3) {
req.oracleMobile.connectors.Conn3.get("Resource3").then(
function(success3) {
// handle success
},
function(error3) {
// handle error
}
}).then(
function(result4) {
// and so on
}
);
As you can see the nesting hell is removed, and now each connector call within the then() has a success or error handler, where you can pass results to the next then() function and so on.
The then() function isn't where the Promise supports ends, the server side SDKs also include:
- all: Wait for an array of promises to all complete and return a result that has all results.
- join: Run a fixed set of promises and callback a function with their results as arguments.
- settle: Same as all, except that it doesn’t stop on the first rejection.
As noted in the documentation these are modeled on the 3rd party Bluebird Promises implementation. Indeed you are free to import Bluebird as a 3rd party module into your MCS server side custom API node code and make use of its extensive Promise support.
Overall with the latest release of Oracle Mobile Cloud Service we continue to strive to simplify the coding experience and empower developers to use the latest programming paradigms, all with the goal of building mobile applications faster. I promise 😉
#boomboom
Image courtesy of artur84 at FreeDigitalPhotos.net