New Rule: All paths in functions should resolve or reject a JQuery deferred
HamletDRC opened this issue · 3 comments
New Rule: All paths in functions should resolve or reject a JQuery deferred
This code is OK:
function myMethod() : JQueryPromise<void> {
var deferred: JQueryDeferred<void> = jQuery.Deferred<void>();
if (something) {
deferred.resolve();
} else {
if (somethingElse) {
deferred.resolve();
} else {
deferred.reject();
}
}
return deferred.promise();
}
This code triggers a violation because not all paths resolve or reject the deferred:
function myMethod() : JQueryPromise<void> {
var deferred: JQueryDeferred<void> = jQuery.Deferred<void>();
if (something) {
deferred.resolve();
} else {
if (somethingElse) {
deferred.resolve();
}
}
return deferred.promise();
}
Limitations
Detecting Deferred instantiation - It is not possible to know conclusively when a new instance of JQuery deferred is created. For the purposes of this rule, we consider "$.Deferred" and "jquery.Deferred" to create a new Deferred instance. The "jquery" token will be case-insensitive (so jquery.Deferred and JQUERY.Deferred will both be assumed to be returning new instances). A deferred passed as a parameter or returned as the result of some other function will not be considered a new instance.
Deferred reference escaping - If the deferred object escapes the method (i.e. it is passed to some other function) then we assume it is resolved/rejected on that code path.
Nested Scopes - If the Deferred object is referenced in a nested scope, then we will assume that the nested scope is always invoked. Consider this code:
function myMethod() : JQueryPromise {
var deferred: JQueryDeferred = jQuery.Deferred();
invokeSomething(() : void => {
deferred.resolve();
});
return deferred.promise();
}
Does the function passed to "invokeSomething" get invoked always or only some time? It is possible in this scenario that invokeSomething does not call the callback, and we then have a path through the function that does not invoke resolve/reject. Consider this code as well:
function myMethod() : JQueryPromise<void> {
var deferred: JQueryDeferred<void> = jQuery.Deferred<void>();
if (someArray.length > 0) {
_.each(someArray, () : void => {
deferred.resolve();
});
} else {
deferred.reject();
}
return deferred.promise();
}
To someone who "knows" underscore, it looks like resolve/reject will always invoked. However, there is no way for us to know that _ represents what is in underscore.d.ts so we cannot assume the _ points to the underscore.js library.
I would be nice to have a similar rule for ES6 promises as well.
@egamma Do you have an opinion about what should happen inside of a while-loop, a for-loop, or a for-in loop?
Do we assume that the loop will always execute at least once? Does this create a violation?
var deferred: JQueryDeferred<void> = jQuery.Deferred<void>();
while(something) {
deferred.resolve();
}
return deferred.promise();
What about a branch that appears in one of these loops. Do we assume that the while-loop always iterates and there there is always at least one iteration there somethingElse is true?
var deferred: JQueryDeferred<void> = jQuery.Deferred<void>();
while(something) {
if(somethingElse) {
deferred.resolve();
}
}
return deferred.promise();
This rule is implemented in 0.0.5 with the described limitations.