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
deferred.resolve()
If getServerTimestamp()
gets called again later, it will check if there is a time difference stored.
deferred.resolve()
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 deferred
s and promise
s.)
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 getTimestamp
and doSomething
already return a deferred promises, we do not need to create a new one within myFunction
. Furthermore, I do not think there is any value to the error handlers within myFunction
because 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, myFunction
would have the same contract with its callers if it were simplified to:
function myFunction() {
return getServerTimestamp().then(doSomething);
}
The only behavioral change is that it does not log the error if either getServerTimestamp
or doSomething
reject with one. If you really want myFunction
to log errors, you could do that with:
function myFunction() {
return getServerTimestamp()
.then(doSomething)
.catch(err => {
console.log(err);
throw err;
});
}
Here is an example fiddle.