I have got two JS functions, both return jQuery's promise. Let's say they are: getServerTimestamp() and, say, doSomething().
getServerTimestamp(), on its first call, basically
- gets the current time on server via ajax
- works out difference between local and server time
- stores the time difference locally
- return the server time using
deferred.resolve()
If getServerTimestamp() gets called again later, it will check if there is a time difference stored.
- If so, it will work out the server time using the stored time difference and return the calculated server time using
deferred.resolve() - If not, it will run like it has been called for the first time.
So I have to call doSomething() in a function which depends on getServerTimestamp(), and because getServerTimestamp() is returning promise, I have to also return a promise for the function. In short, my function looks a bit like this: (I admit I am not great working with multiple deferreds and promises.)
function myFunction() {
var dfd = jQuery.Deferred();
getServerTimestamp().then(
function (serverTime) {
doSomething().then(
function (...) {
...
dfd.resolve(...);
},
function (errorMessage) {
...
console.log(errorMessage);
dfd.reject(errorMessage);
}
);
},
function (errorMessage) {
...
console.log(errorMessage);
dfd.reject(errorMessage);
}
);
return dfd.promise();
}
I am thinking chaining all the promises this way looks a bit clunky, especially the error handling parts. I am wondering if there is an elegant way of doing this.
Another thing I have in mind is that - because getServerTimestamp() practically only makes ajax call once, it would just work everything out locally without further ajax call after its first successful return. In terms of having good code, would it be a better practice if I break down and factor out getServerTimestamp() and do it like the following?
function myFunction() {
var dfd = jQuery.Deferred();
if (isTimeDifferenceReady()) {
doSomething(getServerTimestampFromLocal());
dfd.resolve(...);
} else {
getServerTimestamp().then(function() {
doSomething(serverTime);
dfd.resolve(...);
}, ...);
}
return dfd.promise();
}
This way, I don't have to worry about handling errors from getServerTimestamp() when ajax call is not needed. But I feel like writing the code twice this way. Or should I just forget about making a mess this way? Would there be a more elegant alternative?
I would strongly recommend against tracking the state of the promise with the
isTimeDifferenceReady()function. This would add a lot of unnecessary complexity to your code. One of the beautiful things about Promises is that they allow the caller not to worry about state concerns like whether or not some data needs to be fetched. The caller can rely on the fact that the Promise will resolve when the data is available; and that may be almost immediately or after some longer time.However, we can simplify your
myFunction.Since
getTimestampanddoSomethingalready return a deferred promises, we do not need to create a new one withinmyFunction. Furthermore, I do not think there is any value to the error handlers withinmyFunctionbecause they are just logging the error and then re-rejecting it. If we removed these handlers, the caller would still need to handle these errors.Therefore,
myFunctionwould have the same contract with its callers if it were simplified to:The only behavioral change is that it does not log the error if either
getServerTimestampordoSomethingreject with one. If you really wantmyFunctionto log errors, you could do that with:Here is an example fiddle.