How do I handle errors with promises? - javascript

As a node programmer. I'm used to use "nodebacks" for handling errors in my code:
myFn(param, function(err, data) {
if (err){
//error handling logic
}
else {
// business logic
}
});
When writing that function, I can do something like:
var myFn = function(param, callback){
var calc = doSomeCalculation(param);
if(calc === null) { // or some other way to detect error
callback(new Error("error with calculation"), null);
}
...
someAsyncOp(calcN,function(err, finalResult){
if(err) return callback(err, null);
callback(null, finalResult); // the error is null to signal no error
});
};
How would I do this sort of error handling with promises?

Rule of Thumb
Whenever you have a doubt about how to do something with promises - think about the synchronous version.
try{
var result = myFn(param);
// business logic with result
} catch(e) {
//error handling logic
}
This, at least to me looks a lot cleaner than a callback with a first parameter that is sometimes null.
The promises way is almost always very similar to the synchronous version of the problem:
myFn(param).then(function(result){
// business logic with result
}).catch(function(e){
//error handling logic
});
Where myFn would look something like when working with callbacks:
var myFn = function(param){
return new Promise(function(resolve, reject){
var calc = doSomeCalculation(param);
if(calc === null) { // or some other way to detect error
reject(new Error("error with calculation"), null);
}
someAsyncOp(calcN,function(err, finalResult){
if(err) reject(err);
resolve(finalResult);
})
});
};
Working with callbacks/nodebacks
This is only something you should have to do when working with callbacks, when working with promises it is a lot simpler, and you can do:
var myFn = function(param){
var calc = doSomeCalculation(param);
...
return someAsyncOp(calcN); // returning a promise.
}
Moreover, when working inside promise chains, you get throw safety:
myFn(param).then(function(calcN){
// here, you throw to raise an error and return to resolve
// new Promise should be used only when starting a chain.
}).catch(function(err){
// handle error
}).then(function(){
// ready to go again, we're out of the catch
});
Note, some libraries like Bluebird , RSVP and Q offer syntactic sugar and automatic promisification of methods so you rarely have to use new Promise yourself.
Also, consider reading this and that to learn more about promise error handling.

If you're using the async/await syntax, you can just use the regular try-catch syntax for error handling.
// your promise function
const myFn = function(param){
return new Promise(function(resolve, reject){
if (someLogic()) {
resolve(someValue);
} else {
reject('failure reason');
}
});
}
// Define the parent function as an async function
async function outerFn(param) {
try {
// Wait for the promise to complete using await
const result = await myFn(param)
// business logic with result
} catch (e) {
//error handling logic
}
}

Related

Implementing Simple Promise's finally method

I was trying to implement promise from scratch.
Question:
I wasn't sure how do I implement Finally? (I'm guessing finally will
execute after then's and the catch is invoked. (ONLY once))
If you feel any refactors can be made to my code, please feel free to suggest. This is my naive attempt to implement promises.
This is my implementation:
function Promisify(fn) {
let status = 0; // 0 = unfulfilled, 1 = resolved, 2 = rejected
let result;
let error;
let thenFns = [];
let catchFns = [];
let finallyFn = undefined;
// Public Methods.
this.then = function(fn) {
thenFns.push(fn);
doThen();
return this; // for chaining
};
this.catch = function(fn) {
catchFns.push(fn);
doCatch();
return this; // for chaining
};
// TODO: Implement finally
this.finally = function(fn) {
finallyFn = fn;
// dofinally(fn);
return this;
}
// Private Methods
function resolve(r) {
if (status) throw Error('can not resolve, already handled');
status = 1;
result = r;
doThen();
}
function reject(e) {
if (status) throw Error('can not reject, already handled');
status = 2;
error = e;
doCatch();
}
function doThen() {
if (status === 1) {
while(thenFns.length) {
thenFns.shift()(result);
}
}
}
function doCatch() {
if (status === 2) {
if (catchFns.length === 0) {
console.error('uncaught error')
}
while(catchFns.length) {
catchFns.shift()(error);
}
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
// ======== QUESTION: Caller ================
const demoFail = new Promisify((resolve, reject) => {
setTimeout(function() {
reject('Howdy! from DemoFail')
}, 1000);
});
demoFail
.then(val => console.log("DemoFail Then!!"))
.catch(console.error) //Should throw an error.
.then(val => console.log("Second then!"))
.catch(
(err) => {
throw new Error('error', err);
})
.finally(val => console.log("Executed Finally"))
Here are some things missing from your implementation:
.then() needs to return a new promise, not the same promise.
The return value of a .then() handlers (if it's not a promise) becomes the resolved value of the newly returned promise returned in step #1.
If the return value of a .then() handler is a promise, then the newly returned promise from step 1 doesn't resolve until the promise returned from the .then() handler resolves or rejects with that value.
When calling a .then() handler, you need try/catch around the call to the handler because if the handler throws, then that turns the promise returned into step #1 into a rejected promise with the throw error as the reject reason.
There is similar logic for all the .catch() handlers (it also returns a new promise and the return value of the handler affects the newly returned promise from step #1).
When you store .then() or .catch() callback fns, you also need to store the newly create promise that you returned separate for each callback because that promise will be affected by the return value or exception thrown in the callback.
The spec itself that covers this is fairly simple. You can read it here. And, here's an implementation of that spec.
Your simple version looks like it probably works for one level of promise like fn().then(), but it doesn't do proper chaining and doesn't catch exceptions in handlers and doesn't pay attention to return values in handlers which are all pretty fundamental behaviors of promises. Unfortunately, there is no super simple way to write promises that includes the fundamental behaviors. It just takes more code to support them.

Catch an error inside of Promise resolver

I am not able to catch the Exception/Error that is happening inside of the Resolve promise.
Can someone explain if there is a way to catch this error:
createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
promise = createAsync()
promise.then(function myresolver(){
throw Error('wow')
})
// Catching the error here
try {
promise.resolve()
} catch(err) {
console.log( 'GOTCHA!!', err );
}
EDIT:
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part:
// This is my API user does not have access to this part
promise = createAsync()
promise
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
return Promise.reject(err)
})
Now the solution I would like to give him, but does not work:
// Here is where the user can resolve the promise.
// But must be able to catch the error
promise.catch(function(err){
console.log("GOTCHA", err)
})
promise.resolve('This must be a function but is a string');
Any ideas?
More info:
Yes is an antipattern for the common usage. This is a remote procedure call API, so the user must be able to reject or resolve. And the calls are async so the best approach is to use promises. I wouldn't say is an antipattern for this case. Actually is the only pattern. (I can't use Async/Await)
Let me explain me better, I'm trying to create an API and the user only have access to the promise resolve/reject part.
Now the solution I would like to give him does not work.
Any ideas?
Don't put everything onto that initial promise object. It sounds what you really need to expose to your user is a) a way to resolve (which includes rejection) and b) a way to get the result (promise) back. So all you need to do is give him a function:
function createAsync() {
var resolve;
var promise = new Promise(function(r) { resolve = r; })
.then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
return function(value) {
resolve(value);
return promise;
};
}
and he'd use it like
var start = createAsync();
// possibly later
start('This must be a function but is a string').catch(function(err){
console.log("GOTCHA", err)
});
// or
start(Promise.reject(new Error('doomed to fail'))).catch(…);
but actually this pattern is still convoluted and much too complicated. Simply give your createAsync function a parameter (that might receive a promise if the user needs to delay passing the fun) and be done with it:
function createAsync(valu) {
return Promise.resolve(val).then(function(fun) {
if (typeof fun != 'function')
throw Error('You must resolve a function as argument')
}).catch(function(err){
// Some internal api tasks...
throw err;
});
}
createAsync('This must be a function but is a string').catch(function(err) {
console.log("GOTCHA", err)
});
// or
createAsync(new Promise(function(resolve) {
// later
resolve('This must be a function but is a string');
})).catch(function(err) {
console.log("GOTCHA", err)
});
Warning: as pointed out by #torazaburo "Exporting" the resolve and reject functions provided to the executor (the function passed to the promise constructor), in your case by polluting the promise object with additional properties, is an anti-pattern. The entire point of the design of the promise constructor is that resolving and rejecting is isolated within the executor. Your approach allows any random person who happens to acquire one of your promise objects to resolve or reject it, which is almost certainly not good program design.
With that warning in mind, your call to promise.resolve() doesn't reject the promise ... your promise.then ... throw will "reject" THAT promise (the one returned by .then) not the one in promise
rewrite your code as follows:
promise = createAsync()
promise // the call to promise.resolve will resolve THIS promise, so .then will execute
.then(function myresolver(){
throw Error('wow')
}) // .then returns a NEW promise, in this case rejected with Error('wow')
.catch(function(e) {
console.log("GOTCHA", e)
});
promise.resolve()
and you'll see the expected GOTCHA in the console
Jaromanda's answer already explained that you must use a rejection handler or catch to handle asynchronous errors using promises.
Also make sure to read torazaburo's comment explaining why your approach is an anti-pattern.
If you really must use this approach and also require try/catch for some reason, ES7 gives you a way via async functions
(async function f() {
const createAsync = function() {
var resolve, reject,
promise = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
};
const promise = createAsync();
promise.resolve();
// Catching the error here
try {
await promise.then(function() {
throw Error('wow')
});
} catch (err) {
console.log('GOTCHA!!', err);
}
})()

Bluebird Promise Cancellation

Say I have the following Promise chain:
var parentPromise = Promise.resolve()
.then(function () {
var condition = false;
if (condition) {
return parentPromise.cancel('valid reason');
} else {
return Promise.resolve()
.then(function () {
var someOtherCondition = true;
if (someOtherCondition) {
console.log('inner cancellation');
return parentPromise.cancel('invalid reason');
}
});
}
})
.catch(Promise.CancellationError, function (err) {
console.log('throwing');
if (err.message !== 'valid reason') {
throw err;
}
})
.cancellable();
The above never enters the catch.
If we swap condition to true, the inner cancellation is never hit, but the catch is still not triggered.
removing the .cancellable at the end , and replacing all instances of parentPromise.cancel() with explicit throw new Promise.CancellationError() "fixes" the problem. What I don't understand is why?
Why was the original approach not working?
I am using bluebird 2.3.11.
cancellable() creates cancellable promises and only they throw CancellationError by default, when cancel function is called with out any reason.
In your case, you are making the promise cancellable only after attaching the catch handler. But the promise is not cancellable yet. So, cancel function call will not raise Promise.CancellationError.
You need to change the structure of the code, like this
then(function(..) {
...
})
.cancellable()
.catch(Promise.CancellationError, function (err) {
...
});
Note: I would recommend promising with its beautiful Promise constructor function. It is similar to the ECMA Script 6 specification.
new Promise(function(resolve, reject) {
...
});

Nested Promises Stuck

The following code gets stuck:
var Promise = require('promise');
var testPromise = function(){
return new Promise(function(fulfill, reject){
element.all(by.repeater('item in menu.items')).first().then(function(el){
console.log('test f');
fulfill(el);
console.log('test fe');
});
});
};
... called by the following:
testPromise().then(function(el){
console.log('test successful '+el);
});
The console prints
test f
test fe
And get stuck no more code is executed. It never reaches the then although fulfill has been called.
if using nested promises is an anti pattern then how do I do the following without a nested promise:
var getMenuItemEl = function(itemName){
return new Promise(function(fulfill, reject){
var elFound;
element.all(by.repeater('item in menu.items')).then(function(els){
async.each(els, function(el, callback){
el.getText().then(function(text){
console.log('getMenuItemEl:'+text);
if(text === itemName){
elFound = el;
}
callback();
});
}, function(err){
console.log('complete '+elFound);
if(elFound){
console.log('fulfill');
fulfill(elFound);
console.log('after fulfill');
}else{
reject('no item found');
}
});
});
});
};
This also gets stuck after the fulfill has been called
if using nested promises is an anti pattern then how do I do the following without a nested promise
You would not use the async library. Since all your methods (element.all(), el.getText() etc) do already return promises, you don't need to go back to node-style error callbacks. From my "rules for promises",
every async function (even if it is a callback) should return a promise, and use your libraries' methods to compose them. You really don't need to call the Promise constructor on your own. That iteration you are doing there can be easily done by a map over the els, then collecting all the single promises together with Promise.all.
function getMenuItemEl(itemName) {
return element.all(by.repeater('item in menu.items'))
.then(function(els){
return Promise.all(els.map(function(el) {
return el.getText()
.then(function(text){
console.log('getMenuItemEl:'+text);
return (text === itemName) ? el : null;
});
}));
})
.then(function(foundEls) {
for (var i=0; i<foundEls.length; i++)
if (foundEls[i] != null) {
console.log('fulfill');
return foundEls[i];
}
throw new Error('no item found');
});
}
The following code solves my problem, mainly due to my lack of knowledge of the protractor api
var doClickMenuItemEl = function(itemName){
return element.all(by.repeater('item in menu.items')).filter(function(elem, index) {
return elem.getText().then(function(text) {
return text === itemName;
});
}).then(function(els){
els[0].click();
});
};
It also says on https://github.com/angular/protractor/issues/379 that
Yes, this is inherited from webdriver promises, which are not quite promise A+ compliant, unfortunately.

Resolving a deferred using Angular's $q.when() with a reason

I want to use $q.when() to wrap some non-promise callbacks. But, I can't figure out how to resolve the promise from within the callback. What do I do inside the anonymous function to force $q.when() to resolve with my reason?
promises = $q.when(
notAPromise(
// this resolves the promise, but does not pass the return value vvv
function success(res) { return "Special reason"; },
function failure(res) { return $q.reject('failure'); }
)
);
promises.then(
// I want success == "Special reason" from ^^^
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
The functionality I want to duplicate is this:
promises = function(){
var deferred = $q.defer();
notAPromise(
function success(res) { deferred.resolve("Special reason"); },
function failure(res) { deferred.reject('failure'); }
);
return deferred.promise;
};
promises.then(
// success == "Special reason"
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
This is good, but when() looks so nice. I just can't pass the resolve message to then().
UPDATE
There are better, more robust ways to do this. $q throws exceptions synchronously, and as #Benjamin points out, the major promise libs are moving toward using full Promises in place of Deferreds.
That said, this question is looking for a way to do this using $q's when() function. Objectively superior techniques are of course welcome but don't answer this specific question.
The core problem
You're basically trying to convert an existing callback API to promises. In Angular $q.when is used for promise aggregation, and for thenable assimilation (that is, working with another promise library). Fear not, as what you want is perfectly doable without the cruft of a manual deferred each time.
Deferred objects, and the promise constructor
Sadly, with Angular 1.x you're stuck with the outdated deferred interface, that not only like you said is ugly, it's also unsafe (it's risky and throws synchronously).
What you'd like is called the promise constructor, it's what all implementations (Bluebird, Q, When, RSVP, native promises, etc) are switching to since it's nicer and safer.
Here is how your method would look with native promises:
var promise = new Promise(function(resolve,reject){
notAPromise(
function success(res) { resolve("Special reason") },
function failure(res) { reject(new Error('failure')); } // Always reject
) // with errors!
);
You can replicate this functionality in $q of course:
function resolver(handler){
try {
var d = $q.defer();
handler(function(v){ d.resolve(v); }, function(r){ d.reject(r); });
return d.promise;
} catch (e) {
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
}
Which would let you do:
var promise = resolver(function(resolve,reject){
notAPromise(function success(res){ resolve("Special reason"),
function failure(res){ reject(new Error("failure")); })
});
promise.then(function(){
});
An automatic promisification helper
Of course, it's equally easy to write an automatic promisification method for your specific case. If you work with a lot of APIs with the callback convention fn(onSuccess, onError) you can do:
function promisify(fn){
return function promisified(){
var args = Array(arguments.length + 2);
for(var i = 0; i < arguments.length; i++){
args.push(arguments[i]);
}
var d = $q.defer();
args.push(function(r){ d.resolve(r); });
args.push(function(r){ d.reject(r); });
try{
fn.call(this, args); // call with the arguments
} catch (e){ // promise returning functions must NEVER sync throw
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
return d.promise; // return a promise on the API.
};
}
This would let you do:
var aPromise = promisify(notAPromise);
var promise = aPromise.then(function(val){
// access res here
return "special reason";
}).catch(function(e){
// access rejection value here
return $q.reject(new Error("failure"));
});
Which is even neater
Ok here's my interpretation of what I think you want.
I am assuming you want to integrate non-promise callbacks with a deferred/promise?
The following example uses the wrapCallback function to wrap two non-promise callbacks, successCallback and errCallback. The non-promise callbacks each return a value, and this value will be used to either resolve or reject the deferred.
I use a random number to determine if the deferred should be resolved or rejected, and it is resolved or rejected with the return value from the non-promise callbacks.
Non angular code:
function printArgs() {
console.log.apply(console, arguments);
}
var printSuccess = printArgs.bind(null, "success");
var printFail = printArgs.bind(null, "fail");
function successCallback() {
console.log("success", this);
return "success-result";
}
function errCallback() {
console.log("err", this);
return "err-result";
}
function wrapCallback(dfd, type, callback, ctx) {
return function () {
var result = callback.apply(ctx || this, arguments);
dfd[type](result);
};
}
Angular code:
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q) {
var dfd = $q.defer();
var wrappedSuccess = wrapCallback(dfd, "resolve", successCallback);
var wrappedErr = wrapCallback(dfd, "reject", errCallback);
var rnd = Math.random();
var success = (rnd > 0.5);
success ? wrappedSuccess() : wrappedErr();
console.log(rnd, "calling " + (success ? "success" : "err") + " callback");
dfd.promise.then(printSuccess, printFail);
}
Example output where the random number is less than 0.5, and so the deferred was rejected.
err Window /fiddlegrimbo/m2sgu/18/show/
0.11447505658499701 calling err callback
fail err-result

Categories