Why is node.js/bluebird eating an exception? - javascript

Deep within my promise stack, I make this call:
function isNameAvailable(name) {
return registry.getName(name)
.then(function(result) {
return result ? false : true;
});
}
Unfortunately, and this is a programming error, registry was undefined. My node.js application did not print any error message. Any ideas why? I am using the bluebird promise library.
Edit
Here's the calling code. I just added the catch, but it's not catching anything.
function _checkAvailability(name) {
return isNameAvailable(name)
.then(function(isAvailabile) {
if (isAvailabile) {
return true;
}
else {
throw new NameNotAvailable('Name "' + name + '" is not available');
}
})
.catch(function(error) {
console.log('isNameAvailable threw', error);
throw error;
})
}
The stack should eventually roll back to the function that was called by express.js as a result of an HTTP request. That's one place where I am catching all errors and printing a stack trace (but obviously it is not printing anything):
function createUser(req, res) {
userService.createUser(req.body)
.then(function(user) {
res.status(201).send(user);
})
.catch(function(error) {
log.trace(error);
res.status(500).send({'message': error.toString()});
});
}

Edited to reflect your update:
Naresh is right, you need to return a rejected promise from is name available if it throws. You could try
function isNameAvailable(name) {
try {
return registry.getName(name)
.then(function(result) {
return result ? false : true;
});
} catch(e){
return Promise.reject(e)
}
}
See this post for error swallowing in deep promises.
http://www.mattgreer.org/articles/promises-in-wicked-detail/#promises-can-swallow-errors-

Related

Unhandled exceptions with async await

I am trying to convert my old callback style functions to async await. However I can't understand how can I catch unhandled exceptions.
For example let's say I have a function
apiCall(input, function(error, result) {
if (error) {
console.log(error);
} else {
console.log(result);
}
});
I converted to Promise
function test1(input) {
return new Promise(function(resolve, reject) {
apiCall(input, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
Then I call it
test1(4)
.then(function(result) {
console.log('Result: ' + result);
})
.catch(function(errorr) {
console.log('My Error: ' + errorr);
});
Even though I try to return error, sometimes this function crashes. Let's say disk error, JSON parsing error etc. Some error that I didn't handle. I can only catch those errors with
process.on('uncaughtException', function(error) {
console.log('uncaughtException' + error);
});
Is there a way for me to catch all kinds of error with async await?
EDIT: Here is the full github repo for you to try
https://github.com/tosbaha/promise
Run node testme.js and see that it crashes and exception handler doesn't run.
The file that may crash is this Any function may crash but I can't foresee every kind of error. That is why I am looking for a solution to catch an error inside this file.
If you run the code in my repo with node testme.js you will get the following error
results[trackingId] = trackingArray.doesntExist.Something;
^
TypeError: Cannot read property 'Something' of undefined
As you see that catch handler doesn't catch the error.
If apiCall can crash without calling the callback (with an error), I assume it throws some error that can be handled outside it with a try... catch block (although I'm not sure, because I don't know the internal code of apiCall).
You can try the following:
function test1(input) {
return new Promise(function(resolve, reject) {
try {
apiCall(input, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
} catch (e) {
// reject the errors not passed to the callback
reject(e);
}
});
}

Break out of an array of Promises while in .settle() (or equivalent)

We're currently using bluebird v2.9.8, unable to upgrade to v3 for compatibility (for now, but that might not have a solution either).
We've made use of .settle() in the past, but we've hit a case where we have a set of users, mapped to promises, that we need to confirm whether a specific field is true.
If there's a single case of false, then there's no need to continue. If they were all true that would mean we had executed all promises.
Promise.settle() will execute all, waiting until all are complete.
Again, the goal is to break as soon as we get a false.
Turns out an additional piece of the code was calling an additional Promise to get more info from the db. So, rewritten to use Promise.all():
var accessPromises = users.map(function (user) {
return new Promise(function(resolve, reject) {
if (user.userId == matchingUserId) {
return resolve(true);
} else if (user.type && user.type == matchingType) {
return resolve(true);
} else {
// See if this user is one of your connections
DB.getAdditionalUserInfo()
.then(function (additionalUserInfo) {
if (additionalUserInfo.a == user.userId)
return resolve(true);
else
return reject(false);
})
.catch(function (err) {
return reject(false);
});
}
});
});
Promise.all(accessPromises).then(function (accessResults) {
if (accessResults.every(result => result)
res.ok();
else
res.notFound();
})
.catch(function (err) {
res.notFound();
});
This does allow us to break after the 1st rejection, but any of the additional DB calls that were already started complete anyway.
This will work, and allow us to get the response back to the client faster, but still leaves a bit of wasted processing.
Use Promise.all() instead of Promise.settle().
Promise.all() will finish when the first promise it is passed rejects and will not wait for the rest. So, you can test your condition and then reject and then Promise.all() will also reject immediately.
Promise.settle(), on the other hand, will wait until all requests finish, regardless of outcome.
If you showed some representative code, we could help you much more specifically.
Here's a made up example:
function getUser(name) {
// code that returns a promise whose fulfilled value is a user object
}
function getUserTestField(name) {
return getUser(name).then(function(user) {
if (!user.someField) {
return Promise.reject({status: false});
} else {
return user;
}
}, function(err) {
return Promise.reject({errCode: err});
});
}
var promises = ["bob", "ted", "alice"].map(function(name) {
return getUserTestField(name);
});
Promise.all(promises).then(function(users) {
// all users had field set to true
}, function(err) {
if (err.status === false) {
// at least one user has the field set to false
} else {
// some other type of error here
console.log(err.errCode);
}
});

Promise code are read twice

I use the following code to read json file and return a promise
I've two questions
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
return JSON.parse(res);
},
function (err) {
throw new Error("Error :" + err);
}).then(function () {
console.log("test2");
});
});
I use the console log and I see that the console is printed twice
test
test
test2
test2
why its happening and how to avoid it ?
In the place I've put console.log("test2"); I need to invoke event
that the json parse is finished and still return outside the json object (to the caller), when I add the last then it doesn't work(the returned object is undefined),any idea how to do that right?
UPDATE I try like following which it doesn't work...
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
JSON.parse(res); //data parse
}.catch(function (err) {
throw new Error("Error :" + err);
}
).then(function (data) {
obj.emit('ready');
return data;
}))
});
}
UPDATE2 I was able to solve it by simply add new return JSON.parse(res);
Now how should I solve the first issue which method called twice
Like #jaromandaX said, you probably got two *.json files. Try to print out the file name instead and it should become more obvious. In that case, .map is expected to be called twice, once for each file. Otherwise you aren't gonna be able to read and parse two files together.
If you want to get it to converge to a single point after all file reads and parses are complete, then you need to chain another .then after .map. eg.
return globAsync("folder/*.json")
.map(function(file) {
...
})
.then(function() {
obj.emit('ready');
});
EDIT To answer your question in comment. There are a few things you should keep in mind.
Throwing Error inside the promise chain will get caught by the promise and send it into the rejection flow. You may still throw an error if you are interested in getting custom error type or printing stack trace in a desirable way. But most people prefer return Promise.reject(error).
Any rejection in .map will send the promise chain into rejection flow.
Inside the rejection chain, if you want to continue down the rejection flow. You need to return Promise.reject(error), otherwise if you don't return a reject object, you can bring it back into resolve flow.
If you want to want to handle each error individually, you can do something like this:
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.map(function(file) {
return fs.readFileAsync(file, 'utf8')
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(res) {
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});
If you want to handle once for glob and once for file read, then you have to get a bit more creative.
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(files) {
return Promise.resolve(files)
.map(function(file) {
return fs.readFileAsync(file, 'utf8');
})
.catch(function(error) {
// TODO: Handle error once for any read error
return Promise.reject(error);
})
.map(function(res) {
// Judging by your original code, you are not handling
// parser error, so I wrote this code to behave equivalent
// to your original. Otherwise chain parse immediate after
// readFileAsync.
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});

How can you retry after an exception in Javascript when using promises?

I'm using the Bluebird promise library. I have a chain of promisified functions like the following:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.spread(function(response, data) {
return sendResponseAsync(response);
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// handle error here
});
Occasionally sendMessage will fail because, let's say, the server to respond to isn't available. I want the code to keep on trying to respond forever until it succeeds. You can't simply wrap the sendMessage in a catch because it doesn't actually throw an exception, I suppose, it calls the "error" function which, in this promisified code is the "catch" at the bottom. So there must be some way to "retry" send message in the "catch" section. The problem is that even if I retry in a loop in the "catch" I still have no way to jump up to the promise chain and execute the remaining promisified functions. How do I deal with this?
EDIT:
My retry for a HTTP post ended up looking like this:
function retry(func) {
return func()
.spread(function(httpResponse) {
if (httpResponse.statusCode != 200) {
Log.error("HTTP post returned error status: "+httpResponse.statusCode);
Sleep.sleep(5);
return retry(func);
}
})
.catch(function(err) {
Log.err("Unable to send response via HTTP");
Sleep.sleep(5);
return retry(func);
});
}
Here's a sample retry function (not yet tested):
function retry(maxRetries, fn) {
return fn().catch(function(err) {
if (maxRetries <= 0) {
throw err;
}
return retry(maxRetries - 1, fn);
});
}
The idea is that you can wrap a function that returns a promise with something that will catch and retry on error until running out of retries. So if you're going to retry sendResponseAsync:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.spread(function(response, data) {
return retry(3, function () { return sendResponseAsync(response); });
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// handle error here
});
Since the retry promise won't actually throw until all retries have been exhausted, your call chain can continue.
Edit:
Of course, you could always loop forever if you preferred:
function retryForever(fn) {
return fn().catch(function(err) {
return retryForever(fn);
});
}
Here is a small helper that acts like then but retries the function.
Promise.prototype.retry = function retry(onFulfilled, onRejected, n){
n = n || 3; // default to 3 retries
return this.then(function(result) {
return Promise.try(function(){
return onFulfilled(result); // guard against synchronous errors too
}).catch(function(err){
if(n <= 0) throw err;
return this.retry(onFulfilled, onRejected, n - 1);
}.bind(this)); // keep `this` value
}.bind(this), onRejected);
};
Which would let you write your code prettier like:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.retry(function(response, data) {
return sendResponseAsync(response); // will retry this 3 times
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// I don't like catch alls :/ Consider using `.error` instead.
});
I just released https://github.com/zyklus/promise-repeat, which retries a promise until it either times out or a maximum number of attempts are hit. It allows you to write:
receiveMessageAsync(params)
...
.spread(retry(
function(response, data) {
return sendResponseAsync(response);
}
))
...

Recursive Promise Call- Memory Scope Variable Issue

I have these functions for the purpose of retrieving a token through a api call out. If the user enters the wrong password, the promise will reject and on reject the function is called again to give the user another try.
If the user enters the right password the first time, there is no issue.
But if the user enters a wrong password and tries again...but tries again successfully, I am having a memory issue. Because of the recursive call to callApiToken() on the second try the promise is fullfilled and callApiToken().then(function() { refreshToken(); }) is called. file.token = JSON.parse(tokenString); is completed but in a different memory scope. Not sure what to do about this. I say this because the routine runs successfully. But the global var file is not populated as it should be.
createTokenFile() is called first.
var file = {};
function createTokenFile() {
block = true;
callApiToken()
.then(function() { refreshToken(); }) // ON THE SECOND RECURSIVE
.catch(function() { // RUN refreshToken() IS CALLED
callApiToken();
}).finally(function() {
block = false;
});
}
function refreshToken() {
var tokenFileAbsolute = path.join(__dirname, 'token-file.json');
return fs.readFileAsync(tokenFileAbsolute, {encoding: 'utf-8'})
.then(function(tokenString) {
file.token = JSON.parse(tokenString);
}).catch(function(err) {
console.log("No token-file.json file found. " .red +
"Please complete for a new one." .red);
createTokenFile();
});
}
UPDATE with other promise code that gives resolve for callApiToken()which is actually getCredentials:
Note: fs.writeFileAsync(tokenFile, token) does complete successfully on the second recursive call.
function getPassword(user) {
return readAsync({prompt: "Password: ", silent: true, replace: "*" })
.then(function(pass) {
return postAsync(URL, payload(user[0], pass[0]));
});
}
function getCredentials() {
return readAsync({prompt: "Username: "}).then(getPassword);
}
function writeToFile(data, response) {
tokenFile = path.join(__dirname, 'token-file.json');
token = JSON.stringify({
id: data.access.token.id,
expires: data.access.token.expires
});
return fs.writeFileAsync(tokenFile, token).then(function(err) {
if (err) throw err;
console.log("Token was successfully retrieved and written to " .cyan +
tokenFile .cyan + "." .cyan);
});
}
There is no such thing as a "memory scope". You simply have a timing issue!
If an action is asynchronous, you always have to return a promise from the function when you want to wait for the result - and you seem to do.
var file = {};
function createTokenFile() {
block = true;
callApiToken()
.then(function() {
return refreshToken();
// ^^^^^^ here
})
.catch(function() {
return callApiToken();
// ^^^^^^ and here
}).finally(function() {
block = false;
});
}
function refreshToken() {
var tokenFileAbsolute = path.join(__dirname, 'token-file.json');
return fs.readFileAsync(tokenFileAbsolute, {encoding: 'utf-8'})
.then(function(tokenString) {
file.token = JSON.parse(tokenString);
}).catch(function(err) {
console.log("No token-file.json file found. " .red +
"Please complete for a new one." .red);
return createTokenFile();
// ^^^^^^ and here!!!
});
}
Btw, my guess is that your recursion is flawed. Don't you want refreshToken to reject, and createTokenFile to call itself from within itself (instead of the second callApiToken())?

Categories

Resources