Best practice to handle exception when using Q.promise - javascript

I have the following method:
module.exports.getId = function(someObject) {
var myId = null;
return Q.Promise(function(resolve, reject, notify) {
// Loop through all the id's
someObject.user.player._id.forEach(function (id) {
if (id.root == "1.2.3.4.5.6") {
myId = id.extension;
}
});
resolve(myId);
});
};
This method works great as long as someObject exists and has the attributes user.player._id.
The problem i'm having is that if someObject is null or does not have all the appropriate nested attributes, an exception is thrown and the promise is never resolved. The only way I actually see the exception is if I have a .fail on the calling function, but that still doesn't actually resolve the promise.
Example of how I currently can see the exception:
myLib.getId.then(function() {
// something
}).fail(function(err) {
console.log(err);
});
I know 2 ways to get around this problem, but i'm not sure which, if either is the best way to handle something like this.
Option 1 (use try/catch inside my Q.promise):
module.exports.getId = function(someObject) {
var myId = null;
return Q.Promise(function(resolve, reject, notify) {
try {
// Loop through all the id's
someObject.user.player._id.forEach(function (id) {
if (id.root == "1.2.3.4.5.6") {
myId = id.extension;
}
});
} catch(e) {
reject(e);
}
resolve(myId);
});
};
Option 2 (explicitly check if someObject.user.player._id exists):
module.exports.getId = function(someObject) {
var myId = null;
return Q.Promise(function(resolve, reject, notify) {
ifi(someObject.user.player._id exists..) {
// Loop through all the id's
someObject.user.player._id.forEach(function (id) {
if (id.root == "1.2.3.4.5.6") {
myId = id.extension;
}
});
resolve(myId);
} else {
reject('invalid object');
}
});
};
Option 1 seems to smell funky to me because i'm using try/catch inside of a promise. Option 2 solves my problem, but any other unexpected exceptions will not get caught.
Is there a better way I should be handling this?

Your first example has a few problems:
When you catch an exception, you are rejecting the promise, then resolving the promise. That's breaking the promise contract; You can get around that by calling resolve within the try, not outside.
By using try/catch, you could be swallowing unintended errors. That is you are assuming that the only error would come from someObject.user.player._id not existing. That may be true at the moment, but it's not guaranteed to remain true as your code evolves.
By testing exactly for the known error condition, you know you won't be swallowing unexpected errors. Therefore, I would use your second example.

Related

Why do JavaScript promises chain rejections into resolved promises?

I'm wondering why rejections are chained as resolved with ES6 promises?
Here's an example:
let p = Promise.reject().then(function () {
return 'one';
}, function() {
return 'two';
});
p.then(function (value) {
// I do not expect this to be executed, since it was not resolved.
console.log(value);
});
The above outputs "two" to the console.
https://jsfiddle.net/pscb88xg/1/
Why does the chaining of a promise mutate a rejection into a successful resolve?
Edit: I want to clarify that the question has practical application.
What if you want to convert data from A to B using chaining.
p.then(function (A) {
return new B(A);
});
The above mutates rejections into resolved values. Even if no reject callback is used.
For example;
let p = Promise.reject('error').then(function (A) {
return new B(A);
});
// some code elsewhere
p.then(function (B) {
console.log('success!');
});
In the above example. The value B is not B but the error, and it was resolved successfully later in the chain.
Is this normal?
Edit: I understand my confusion now. I was extracting HTTP header values in rejections like this.
let p = myHttpService.get(...).then(function() {
//....
}, function(response) {
// get headers
});
The above was chaining my promises to a resolved value, and I didn't understand why. I can fix my code with the following.
let p = myHttpService.get(...).then(function() {
//....
}, function(response) {
// get headers
return Promise.reject(response);
});
After handling an error you usually want your code to continue, similar to how code after a catch block runs like normal, whereas uncaught exceptions abort.
If you want to abort instead then don't handle the error until the end of the chain:
let p = Promise.reject().then(function () {
return 'one';
});
p.then(function (value) {
// This won't run, because the rejection hasn't been handled yet
console.log(value);
}, function() {
return console.log( 'there was a problem' );
}).then(function ( ) {
// This will run because the rejection has been dealt with already.
console.log( 'Moving on');
});
MDN documentation for Promise.prototype.then says:
After the invocation of the handler function [the function passed to then()], the promise returned by then gets resolved with the returned value as its value.
It's meant to allow you to gracefully recover from an error in a promise chain.
An example might be the 304 Not Modified response from the server. If you were to use a promise based library to do an http request any response that's not 2XX will be considered a failure and the promise will be rejected. From an application's point of view however 304 might just as good as a 200 and you'd like to continue as normal.
This is the same behavior as AngularJS's $q provider.
The mutation occurs because in your rejection handler, you are returning a value and not a rejected promise. If you were to instead, pass a rejected promise, it would behave how you were expecting:
let p = Promise.reject().then(function () {
return 'one';
}, function() {
return Promise.reject('two');
});
p.then(function (value) {
// I do not expect this to be executed, since it was not resolved.
console.log(value);
}, function() {
console.log("Rejected, baby!");
});

Parse.Query.each() chained promises

I'm writing a background job function on Parse.com CloudCode. The job needs to call the same function (that includes a Parse.Query.each()call) several times with different parameters, and I want to chain these calls with promises. Here's what I have so far:
Parse.Cloud.job("threadAutoReminders", function(request, response) {
processThreads(parameters1).then(function() {
return processThreads(parameters2);
}).then(function() {
return processThreads(parameters3);
}).then(function() {
return processThreads(parameters4);
}).then(function() {
response.success("Success");
}, function(error) {
response.error(JSON.stringify(error));
});
});
Below is the processThreads() function:
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
// do something
});
}
My questions are:
Am I chaining function calls using promises correctly?
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged..
Am I chaining function calls using promises correctly?
Yes.
What happens in threadQuery.each() returns zero results? Will the promise chain continue with execution? I'm asking because at the moment "Hello" never gets logged.
I think I'm right in saying that, if "do something" is synchronous, then zero "Hello" messages can only happen if :
an uncaught error occurs in "do something" before a would-be "Hello" is logged, or
every stage gives no results (suspect your data, your query or your expectation).
You can immunise yourself against uncaught errors by catching them. As Parse promises are not throw-safe, you need to catch them manually :
function processThreads(parameters) {
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
return threadQuery.each(function(thread) {
console.log("Hello");
try {
doSomething(); // synchronous
} catch(e) {
//do nothing
}
});
}
That should ensure that the iteration continues and that a fulfilled promise is returned.
The following example shows as use promises inside your function using a web browser implementation.
function processThreads(parameters) {
var promise = new Promise();
var threadQuery = new Parse.Query("Thread");
threadQuery... // set up query using parameters
try {
threadQuery.each(function(thread) {
console.log("Hello");
if (condition) {
throw "Something was wrong with the thread with id " + thread.id;
}
});
} catch (e) {
promise.reject(e);
return promise;
}
promise.resolve();
return promise;
}
Implementations of promise:
Web Browser https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
jQuery https://api.jquery.com/promise/
Angular https://docs.angularjs.org/api/ng/service/$q

Silent exceptions in Q:then callback?

I have some issue with calling function of null variable in 'then' callback of Q.promise.
The first call (without Q using) will show an error, but while the second (wuth Q using) doesn't.
Small example:
var Q = require('q');
var nul = null;
var exp;
(function (exp) {
var A = (function () {
function A() {
};
A.prototype.foo = function () {
var d = Q.defer();
d.resolve('Hello, world');
return d.promise;
};
A.prototype.bar = function (i) {
switch (i) {
case 0:
/**
* That's all ok, "TypeError: Cannot read property 'qqq' of null"
*/
console.log(nul);
nul.qqq();
console.log('ok');
break;
case 1:
/**
* it's not ok, I see only result of "console.log(nul)", line 29
*/
this.foo().then(function () {
console.log(nul);
nul.qqq();
console.log('ok');
});
break;
};
};
return A;
})();
exp.A = A;
}) (exp || (exp = {}));
exp.a = new exp.A();
// You should run functions SEPARATELY!!!
exp.a.bar(0); // in that case: that's all ok, "TypeError: Cannot read property 'qqq' of null"
exp.a.bar(1); // int that case: it's not ok, I see only result of "console.log(nul)", line 29
I don't have any idea how to solve it
The reason why you're not seeing the second error on the console is because Q catches all errors and lets you handle them separately.
You can handle an error in then() by chaining with a catch() function, in your example this can be done this way:
this.foo().then(function () {
console.log(nul);
nul.qqq();
console.log('ok');
}).catch(function(error) {
// do something with error
console.log(error);
});
You can get this behavior also by using a try/catch block inside then() like this:
this.foo().then(function () {
try {
console.log(nul);
nul.qqq();
console.log('ok');
} catch (e) {
console.log(e);
}
});
Old answer
Here are a few options for catching errors in JS/node.js:
Try/Catch blocks
These work like in their Java/C# equivalent, wrap each of the calls you make with a try block and catch an error, handling it in the catch block
try {
exp.a.bar(0);
} catch(e) {
console.log(e);
}
You can also add finally blocks, check the type of an exception/error and so on, you can read more about it on the MDN page
Node.js uncaughtException handler
In node, you can catch all uncaught errors, which will stop your program, by binding a callback to the uncaughtException event like this:
process.on('uncaughtException', function (e) {
console.log('Error: ' + e);
});
This isn't always the best thing to do in a program, but if you really don't want to stop the execution, this is an option.
Finally, I recommend giving a look to this official article for best practices about handling errors in node.js

how to check error in qunit

I have a function in JavaScript that uses the q library:
validateOnSelection : function(model) {
this.context.service.doLofig(model).then(function(bResult) {
if (bResult) {
return true;
} else {
throw new Error(that.context.i18n.getText("i18n", "error"));
}
});
}
How can I check in qunit that the result is error? Let's assume that the result: bResult is false and Error should raise.
I tried:
test("Basic test ", {
// get the oTemplate and model
return oTemplate.validateOnSelection(model).then(function(bResult) {
// Now I need to check the error
});
}));
The problem that I didn't get to the check "// Now I need to check the error"
There are lots of problems here. For one, you don't have any way to let the calling code know that your function has finished. Without that, QUnit can't determine when to run the assertions. Then you'll need to use QUnit's async ability, otherwise the test function finishes before your promise is resolved. Additionally, you can use the throws assertion to check for an error. The example below is using QUnit version 1.16.0 (the newest version).
validateOnSelection : function(model) {
// Instead, return a promise from this method which your calling code can use:
var deferred = Q.defer();
this.context.service.doLofig(model).then(function(bResult) {
if (bResult) {
// return true; this doesn't really do anything, it doesn't return anywhere.
// instead, resolve the promise:
deferred.resolve(true);
} else {
// we don't really want to "throw" here, we nee to reject the promise:
// throw new Error(that.context.i18n.getText("i18n", "error"));
deferred.reject(new Error(that.context.i18n.getText("i18n", "error")));
}
});
return deferred.promise;
}
Now we can set up our test to wait for the promise to finish and then test the result...
QUnit.test("Basic test", function(assert) {
// get the oTemplate and model
var done = QUnit.async(); // call this function when the promise is complete
// where does `model` come from???
oTemplate.validateOnSelection(model).then(function(bResult) {
// Now I need to check the error
assert.ok(bResult instanceof Error, "We should get an error in this case");
done(); // now we let QUnit know that async actions are complete.
});
});

Detect existence of next handler in Angular JavaScript promise chain

Given the following two $resource examples:
var exampleOne = $resource('/path').save(objectOne);
exampleOne.$promise.then(function (success) {}, function (error) {});
var exampleTwo = $resource('/path').save(objectTwo);
exampleTwo.$promise.then(function (success) {});
[NOTE: Example two contains no error handler]
And an interceptor that sits below all $http requests:
var interceptor = ['$location', '$q', function ($location, $q) {
function error(response) {
if (response.status === 400) {
return $q.reject(response);
}
else {
$location.path('/error/page');
}
return $q.reject(response);
}
return {
'responseError': error
};
}
$httpProvider.interceptors.push(interceptor);
How can I make the interceptor not reject when the example resources $promise.then() contain no error callback? If the call back exists as in exampleOne then I wish to reject, but if not as in exampleTwo then I wish to redirect to the error page thus changing the conditional to something like:
if (response.status === 400 && $q.unresolvedPromises.doIndeedExist()) { ...
Why? Because only some situations in my project call for handling a 400 in a user friendly way, thus I'd like to eliminate many duplicate error callbacks or having to place a list of uncommon situations in the interceptor. I'd like the interceptor to be able to decide based on the presence of another handler in the promise chain.
Simply put it is impossible, you can't detect if someone will attach a handler in some point in the future just like you can't tell if when you throw in a function it will be caught on the outside or not. However, what you want done can be done.
It is not a 'noob question', and it is very fundamental:
function foo()
throw new Error(); // I want to know if whoever is calling `foo`
// handles this error
}
First, what you can do
Simply put in the first case:
exampleOne.$promise.then(function (success) {}, function (error) {});
What you get is a promise that is always fulfilled. However, in the second case the promise might be rejected. Handling a rejection with a rejection handler is like a catch in real code - once you handle it it is no longer rejected.
Personally, I would not use an interceptor here, but rather a resource-using pattern since that's more clear with intent, you can wrap it in a function so it won't need a scope but I like that idea less. Here is what I'd do
attempt(function(){
return $resource('/path').save(objectTwo).$promise.
then(function (success) {});
});
function attempt(fn){
var res = fn();
res.catch(function(err){
// figure out what conditions you want here
// if the promise is rejected. In your case check for http errors
showModalScreen();
}
return res; // for chaining, catch handlers can still be added in the future, so
// this only detects `catch` on the function passed directly so
// we keep composability
}
Now, a short proof that it can't be done
Let's prove it for fun.
Let's say we are given the code of a program M, we create a new promise p and replace every return statement in M andthrow statement in M with a return p.catch(function(){}) and also add a return p.catch(function(){}), now a handler will be added to p if and only if running M ever terminates. So in short - given code M we have constructed a way to see if it halts based on an existence of a solution to the problem of finding if catch is appended to p - so this problem is at least as hard as the halting problem.
Maybe you can postpone redirect with zero timeout and give a chance to error handler if any exists to set flag on error object that error was handled:
var interceptor = ['$q', '$timeout', function ($q, $timeout) {
function error(rejection) {
return $q.reject(rejection).finally(function () {
$timeout(function () {
if (rejection.errorHandled === true) {
alert('all is under control');
} else {
alert("Houston we've got problems");
}
}, 0); //zero timeout to execute function after all handlers in chain completed
});
}
return {
'responseError': error
};
}];
var exampleOne = $resource('/path').save(objectOne);
exampleOne.$promise.then(function (success) { }, function(error) {
error.errorHandled = true;
});

Categories

Resources