I've the following mongoose queries which each producing the same result but with different Promise implementations. I've tested the scenario where mongooseModel throws an error and again each implementation produces the same result i.e. the calling method catches the exception and returns the correct error message.
What I'm wondering is which is the best approach? The fnCall is more readable and makes more sense when reading! In my opinion anyway. Whats important here is a repeatable pattern that we can use across all our NodeJS modules.
First way:
ListService.prototype.getItems = function (queryParams, restrictFields, startIndex, pageSize) {
return Promise.fnCall(function() {
return mongooseModel().find(queryParams, restrictFields)
.skip(startIndex)
.limit(pageSize)
.lean(true)
.exec();
})
.then(function (items) { return items; })
.catch(function (error) {return error; });
};
Second way:
ListService.prototype.getItems = function (queryParams, restrictFields, startIndex, pageSize) {
return new Promise(function (resolve, reject) {
mongooseModel().find(queryParams, restrictFields)
.skip(startIndex)
.limit(pageSize)
.lean(true)
.exec(function (err, items) {
if (err) {
reject(err);
}
else {
resolve(items);
}
});
};
The best approach depends on your needs and the environment you are working in. Thus far you have not considered performance or security, only syntax and compatibility. If you need speed, low memory foot print, and trust the code then go with BlueBird. If you do not trust the code i.e. need security, do not mind a heavy memory footprint, and don't mind a nearly 10x slow down then go with Q.
Ref: https://github.com/petkaantonov/bluebird/issues/381
Good on you for testing the results for consistent behaviour. However, the question of which is the 'best approach' depends on what you are seeking.
If you are wanting the fastest result, you should test both scenarios yourself using your own code. You may find that one form will outperform the other slightly and consume less memory. You may also want to look at defer(), if it is supported, as it is sometimes significantly faster.
If speed is not key and your promise library doesn't have a clear convention, then the best pattern is the one that you find the easiest to read. Some people prefer promisifying wrappers (like your fnCall()) over creating a new Promise() over and over, and a lot of people seem to dislike defer() for some reason. But without a convincing performance argument, there is really no good answer.
The last thing worth considering is whether to stray from JS's native implementation of Promises at all, particularly in Node, now that it is officially supported. In this case, you are pretty much restricted to new Promise().
Related
I'm learning asynchronous programming in JS and I couldn't help but noticed both JS and Raku has some construct for asynchronous programming with the same name, however I'm uncertain to what extent the knowledge from one can transfer to the other. I tried reading JS to Raku but the section about async programming is mostly barren.
For example, is it possible to do something like this in Raku?
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => console.log(json))
Or something like this if I want to create my own promises?
function getLanguages() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() >= 0.5;
if (success) {
const languages = ['js', 'perl', 'python', 'raku'];
resolve(languages);
}
else {
reject(new Error('No languages'));
}
}, 0);
});
}
getLanguages()
.then((languages) => {
console.log(languages);
})
.catch((error) => {
console.log(error);
});
A Raku Promise plays the same role as a JavaScript one: it models an operation that may complete asynchronously, with the completion either being successful or erroneous. The API is, however, different, so as to fit in with the Raku language more generally.
In Raku we say that a Promise is either kept (successful completion) or broken (erroneous completion), instead of rejected or resolved. I'm not really sure why that wasn't the case in JavaScript; I've literally never told a person that I've resolved my promise to them!
One can make a new Promise using my $p = Promise.new, but it does not take a function. Rather, one calls $p.keep($value) or $p.break($error). These are actually short for $p.vow.keep($value) and $p.vow.break($error), and if returning the Promise object from an API of some kind, it's usually wise to obtain the vow - the exclusive right to keep or break the Promise, before returning it.
One can use .then, noting that it only takes one function and this is passed the Promise itself, and .result is used to access the result; trying to access the result of a broken Promise will rethrow the exception.
As in JavaScript, using await instead of .then is generally preferred. Unlike in JavaScript, there's no async keyword in Raku: you can await anywhere! So long as you're on a thread pool thread, the entire callstack is saved away and the thread freed up to work on something else, until such a time that the Promise is kept and the continuation is scheduled. As in .Net, you might end up on a different thread after an await. You can await on a non-pool thread (such as the main thread), and then it's a blocking wait on the Promise.
Since Raku has had Promise in the standard library from the first release, asynchronous built-ins are provided entirely in terms of Promise (and Supply for streams of asynchronous values). So the setTimeout equivalent is Promise.interval($seconds), for example.
Overall: the rough idea of what a Promise is in Raku will serve you well, but there's a different API and different idioms to pick up.
I would advise reading the follow pages in the Raku docs :
Promise
Concurrency
Note that Raku doesn't have a built library for doing web requests but there are a few options.
LWP::Simple
HTTP::UserAgent
HTTP::Tiny
All of these are make blocking requests but a simple start will turn the request into a Promise you can chain off. You may also want to checkout Cro which is a full library for building both client and server side systems that are asynchronous from the ground up.
I hope that's enough to get you started. Good luck.
For an introduction to concepts of Parallelism, Concurrency and Asynchrony in Raku, I suggest Jnthn's talk.
Background
I have a REST API using MongoDB, Node.js and Express that makes a request to my NoSQL DB and depending on different results, I want to differentiate which error I send the customer.
Problem
The current version of my code has a generic error handler and always sends the same error message to the client:
api.post("/Surveys/", (req, res) => {
const surveyJSON = req.body;
const sender = replyFactory(res);
Survey.findOne({_id: surveyJSON.id})
.then(doc => {
if(doc !== null)
throw {reason: "ObjectRepeated"};
//do stuff
return new Survey(surveyJSON).save();
})
.then(() => sender.replySuccess("Object saved with success!"))
.catch(error => {
/*
* Here I don't know if:
* 1. The object is repeated
* 2. There was an error while saving (eg. validation failed)
* 3. Server had a hiccup (500)
*/
sender.replyBadRequest(error);
});
});
This is a problem, because the client will always get the same error message, no matter what and I need error differentiation!
Research
I found a possible solution, based on the division of logic and error/response handling:
Handling multiple catches in promise chain
However, I don't understand a few things:
I don't see how, at least in my example, I can separate the logic from the response. The response will depend on the logic after all!
I would like to avoid error sub-classing and hierarchy. First because I don't use bluebird, and I can't subclass the error class the answer suggests, and second because I don't want my code with a billion different error classes with brittle hierarchies that will change in the future.
My idea, that I don't really like either
With this structure, if I want error differentiation, the only thing I can do is to detect an error occurred, build an object with that information and then throw it:
.then(doc => {
if(doc === null)
throw {reason: "ObjectNotFound"};
//do stuff
return doc.save();
})
.catch(error => {
if(error.reason === "ObjectNotFound")
sendJsonResponse(res, 404, err);
else if(error.reason === "Something else ")
sendJsonResponse(/*you get the idea*/);
else //if we don't know the reasons, its because the server likely crashed
sendJsonResponse(res, 500, err);
});
I personally don't find this solution particularly attractive because it means I will have a huge if then else chain of statements in my catch block.
Also, as mentioned in the previous post, generic error handlers are usually frowned upon (and for a good reason imo).
Questions
How can I improve this code?
Objectives
When I started this thread, I had two objectives in mind:
Having error differentiation
Avoid an if then else of doom in a generic catcher
I have now come up with two radically distinct solutions, which I now post here, for future reference.
Solution 1: Generic error handler with Errors Object
This solution is based on the solution from #Marc Rohloff, however, instead of having an array of functions and looping through each one, I have an object with all the errors.
This approach is better because it is faster, and removes the need for the if validation, meaning you actually do less logic:
const errorHandlers = {
ObjectRepeated: function(error){
return { code: 400, error };
},
SomethingElse: function(error){
return { code: 499, error };
}
};
Survey.findOne({
_id: "bananasId"
})
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists."};
//saving empty object for demonstration purposes
return new Survey({}).save();
})
.then(() => console.log("Object saved with success!"))
.catch(error => {
respondToError(error);
});
const respondToError = error => {
const errorObj = errorHandlers[error.reason](error);
if (errorObj !== undefined)
console.log(`Failed with ${errorObj.code} and reason ${error.reason}: ${JSON.stringify(errorObj)}`);
else
//send default error Obj, server 500
console.log(`Generic fail message ${JSON.stringify(error)}`);
};
This solution achieves:
Partial error differentiation (I will explain why)
Avoids an if then else of doom.
This solution only has partial error differentiation. The reason for this is because you can only differentiate errors that you specifically build, via the throw {reaon: "reasonHere", error: "errorHere"} mechanism.
In this example, you would be able to know if the document already exists, but if there is an error saving the said document (lets say, a validation one) then it would be treated as "Generic" error and thrown as a 500.
To achieve full error differentiation with this, you would have to use the nested Promise anti pattern like the following:
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists." };
//saving empty object for demonstration purposes
return new Survey({}).save()
.then(() => {console.log("great success!");})
.catch(error => {throw {reason: "SomethingElse", error}});
})
It would work... But I see it as a best practice to avoid anti-patterns.
Solution 2: Using ECMA6 Generators via co.
This solution uses Generators via the library co. Meant to replace Promises in near future with a syntax similar to async/await this new feature allows you to write asynchronous code that reads like synchronous (well, almost).
To use it, you first need to install co, or something similar like ogen. I pretty much prefer co, so that is what i will be using here instead.
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
The generator function requestHandler will yield all Promises to the library, which will resolve them and either return or throw accordingly.
Using this strategy, you effectively code like you were coding synchronous code (except for the use of yield).
I personally prefer this strategy because:
Your code is easy to read and it looks synchronous (while still have the advantages of asynchronous code).
You do not have to build and throw error objects every where, you can simply send the message immediately.
And, you can BREAK the code flow via return. This is not possible in a promise chain, as in those you have to force a throw (many times a meaningless one) and catch it to stop executing.
The generator function will only be executed once passed into the library co, which then returns a Promise, stating if the execution was successful or not.
This solution achieves:
error differentiation
avoids if then else hell and generalized catchers (although you will use try/catch in your code, and you still have access to a generalized catcher if you need one).
Using generators is, in my opinion, more flexible and makes for easier to read code. Not all cases are cases for generator usage (like mpj suggests in the video) but in this specific case, I believe it to be the best option.
Conclusion
Solution 1: good classical approach to the problem, but has issues inherent to promise chaining. You can overcome some of them by nesting promises, but that is an anti pattern and defeats their purpose.
Solution 2: more versatile, but requires a library and knowledge on how generators work. Furthermore, different libraries will have different behaviors, so you should be aware of that.
I think a good improvement would be creating an error utility method that takes the error message as a parameter, then does all your ifs to try to parse the error (logic that does have to happen somewhere) and returns a formatted error.
function errorFormatter(errMsg) {
var formattedErr = {
responseCode: 500,
msg: 'Internal Server Error'
};
switch (true) {
case errMsg.includes('ObjectNotFound'):
formattedErr.responseCode = 404;
formattedErr.msg = 'Resource not found';
break;
}
return formattedErr;
}
This could be a question with a zillion answers or you'll otherwise need to see my actual code to help me. And it could be that there's one cause (or a small number of causes) for the behavior I'm seeing. First question: Which is it? If the former, I'll withdraw the question so people don't waste their time. I cannot share the code and it's longer than is appropriate here anyway.
I'm writing JavaScript in Node.JS using bluebird for Promises. Parts of my code fit this model.
const Promise = require('bluebird');
function a() {
return new Promise(function(resolve, reject) {
<... do something>
<either> return resolve();
<or> return reject();
})
}
a()
.catch(error => { console.log('Used .catch error=', error) })
.then(result => { console.log('Used .then result=', result) });
In some parts of my code fitting this model, I see the results of one of the log statements. In other parts of my code fitting this model, I see neither. In the latter parts, when I trace execution path with the debugger, after it finishes with a, it puts the green highlight on (error in .catch(error => { and next on } closing a Promise that contains a, its .then, and its .catch, and next (after going through some bluebird code) on } closing a function that contains that same Promise.
JSHint doesn't identify anything related.
I saw very similar behavior when I was using native Promises. I solved it then by substituting bluebird Promises. Now I see bluebird doing the same, though in different places.
If this is something with a known and easily described cause, I'll really appreciate the help. If it's bigger than that, this question probably doesn't belong on Stack Overflow; I'll withdraw it.
Thanks in advance.
Ok, let's start with something completely different. Chances are you should never be using the promise constructor, ever.
The promise constructor is for converting things that are not promise returning into things that are promise returning. If you're using bluebird, you should be using Promise.promisifyAll to do that swiftly and efficiently for you.
Promises are about making your life easier not harder. Writing a ton of boilerplate would beat this purpose. It is likely some frustrating is caused from incorrectly converting a callback API. This is hard to get right - for example, the promise constructor completely ignores return values.
Your code uses catch which recovers from errors. A little like:
try {
var result = doSomething();
} catch (error) {
console.log('Used .catch error=', error);
}
console.log('Used .then result=', result);
Using .catch means you recover from the errors. It means your code can gracefully handle exceptional conditions. If you need to signal in a catch that your code does not recover from the error - you need to rethrow the exception:
.catch(e => { console.log("Got ", e); throw e; })
Since you're using bluebird and modern Node - might I suggest taking a look at generators? http://bluebirdjs.com/docs/api/promise.coroutine.html
Short story:
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
Longer version, described as real life problem (would love someone to read):
I have a library that uses Bluebird (A+ Promise implementation library), to fetch data from database (talking about Sequelize). Every query returns a result, but sometimes it's empty (tried to select something, but there wasn't any). The promise goes into the result function, because there is no reason for an error (not having results is not an error). Example:
Entity.find(1).then(function(result) {
// always ending here, despite result is {}
}).catch(function(error) {
// never ends here
});
I want to wrap this and check if the result is empty (can't check this everywhere in my code). I did this:
function findFirst() {
return Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
return result; // will invoke the success function
} else {
// I WANT TO INVOKE THE ERROR, HOW?! :)
}
}).catch(function(error) {
// never ends here
});
}
findFirst().then(function(result) {
// I HAVE a result
}).catch(function(error) {
// ERROR function - there's either sql error OR there is no result!
});
If you are still with me - I hope you will understand what's up. I want somehow to fire up the error function (where "ERROR function" is). The thing is - I don't know how. I've tried these things:
this.reject('reason'); // doesn't work, this is not a promise, Sequelize related
return new Error('reason'); // calls success function, with error argument
return false; // calls success function with false argument
throw new Error('reason'); // works, but if .catch is missing => BLOW!
As you can see by my comments (and per spec), throwing an error works well. But, there's a big but - if I miss the .catch statement, my whole app blows up.
Why I don't want this? Let's say I want to increment a counter in my database. I don't care about the result - I just make HTTP request.. So I can call incrementInDB(), which has the ability to return results (even for test reasons), so there is throw new Error if it failed. But since I don't care for response, sometimes I won't add .catch statement, right? But now - if I don't (on purpose or by fault) - I end up with your node app down.
I don't find this very nice. Is there any better way to work it out, or I just have to deal with it?
A friend of mine helped me out and I used a new promise to fix things up, like this:
function findFirst() {
var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer
Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
deferred.resolve(result);
} else {
deferred.reject('no result');
}
}).catch(function(error) {
deferred.reject('mysql error');
);
return deferred.promise; // return a promise, no matter of framework
}
Works like a charm! But I got into this: Promise Anti Patterns - wiki article written by Petka Antonov, creator of Bluebird (A+ implementation). It's explicitly said that this is wrong.
So my second question is - is it so? If yes - why? And what's the best way?
Thanks a lot for reading this, I hope someone will spent time to answer it for me :) I should add that I didn't want to depend too much on frameworks, so Sequelize and Bluebird are just things that I ended up working with. My problem is with Promises as a global, not with this particular frameworks.
Please ask only a single question per post :-)
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
No. They just do different things, but once you know that you can choose the appropriate one.
How to use promisify and what are the benefits of it?
I think the Bluebird Promisification docs explain this pretty well - it's used to convert a callback api to one that returns promises.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error?
Yes, throwing an error is totally fine. Inside a then callback, you can throw and it will be caught automatically, resulting to the rejection of the result promise.
You can also use return Promise.reject(new Error(…));; both will have absolutely the same effect.
A friend of mine helped me out and I used a new promise to fix things up, like this: […]
No. You really shouldn't use that. Just use then and throw or return a rejected promise in there.
But if I miss the catch statement - my whole app will blow!
No, it won't. Notice that .catch() method is not a try catch statement - the error will already be caught where your then callback was invoked. It is then only passed to the catch callback as an argument, the .catch() call is not required to capture exceptions.
And when you miss .catch(), your app won't blow. All what happens is that you have a rejected promise laying around, the rest of your app won't be affected by this. You will get an unhandled rejection event, which can be dealt with globally.
Of course, that should not happen; every promise chain (not every promise instance) should be ended with a .catch() that handles errors appropriately. But you definitely don't need .catch() in every helper function, when it returns a promise to somewhere else then errors will usually be handled by the caller.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
And if you use return codes to signal errors instead of exceptions, missing the check will cause subtle bugs and erratic behavior. Forgetting to handle errors is a bug, but having your app blow up in such an unfortunate case is more often better than letting it continue in corrupted state.
Debugging forgotten error code check that is only apparent by the application behaving weirdly at some unrelated-to-the-source-of-error place is easily many orders of magnitude more expensive than debugging a forgotten catch because you have the error message, source of error and stack trace. So if you want productivity in typical application development scenario, exceptions win by a pretty huge margin.
.then(success, fail)
This is usually signal that you are using promises as glorified callbacks where the callbacks are simply passed separately. This is obviously an anti-pattern as you will not get any benefits from using promises in this way. If that's not the case, even then you are just making your code slightly less readable compared to using .catch(), similar to how method(true, true, false) is much less readable than method({option1: true, option2: true, option3: false}).
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Most modules only expose a callback interface, promisify turns a callback interface automatically into a promise interface. If you are writing such code manually (using deferred or new Promise), you are wasting your time.
So my second question is - is it so? If yes - why? And what's the best way?
The best way is to chain the promise:
function findFirst() {
return Entity.find(1).tap(function(result) {
if (!result) throw new Error("no result");
});
}
It's better because it's simpler, shorter, more readable and far less error-prone way to do exactly the same thing.
I have multiple Meteor.calls, where each methods depends on the response of another Meteor method.
Client
Meteor.call('methodOne', function(err, resOne){
if(!err){
Meteor.call('methodTwo', resOne, function(err, resTwo){
if(!err){
Meteor.call('methodThree', resTwo, function(err, resThree){
if(err){
console.log(err);
}
})
}
});
}
});
From Meteor's documentation I know
"Methods called on the client run asynchronously, so you need to pass a callback in order to observe the result of the call."
I know I can create yet another Meteor Method on the server to execute methods 'methodOne', 'MethodTwo', 'MethodThree' wrapped using Meteor.async, or sequentially without the callback all together. But I am worried this path will cause my meteor methods to get bloated and entangled, leading to spaghetti code. I would rather keep each Meteor method simple with one job to do and find a more elegant way of chaining the calls on the client. Any ideas, is there any way to use Promises on the client?
Since the other answer suggests RSVP this answer will suggest Bluebird which is actually the fastest promise library when running real benchmarks. Rather than a micro benchmark that does not really measure anything meaningful. Anyway, I'm not picking it for performance, I'm picking it here because it's also the easiest to use and the one with the best debuggability.
Unlike the other answer, this one also does not suppress errors and the cost of making the function return a promise is marginal since no promise constructor is called.
var call = Promise.promisify(Meteor.call, Meteor);
var calls = call("methodOne").
then(call.bind(Meteor, "methodTwo")).
then(call.bind(Meteor, "methodThree"));
calls.then(function(resThree){
console.log("Got Response!", resThree);
}).catch(function(err){
console.log("Got Error", err);
});
Your approach on the client results in a lot more round trips between the server and the browser. I know you indicated that you were worried about spaghetti code on the server and I don't have visibility into your application as you do, but just going by the example you provide, it seems like an ideal place to wrap all three calls on the server and make only one call from the client, IMHO.
EDIT: You're probably better off looking at #Benjamin Gruenbaum answer, which not only results in better performance, but also provides much more concise code.
Promises - yes there is.
I like RSVP very much, why? Simply because it's the fastest one. (quick benchmark: jsperf ).
Here's quick re-write of your code:
var promise = new RSVP.Promise(function(fulfill, reject) {
Meteor.call('methodOne', '', function(err, resOne) {
if (!err) {
return reject(err);
}
fulfill(resOne);
});
});
promise.then(function(resOne) {
return new RSVP.Promise(function(fulfill, reject) {
Meteor.call('methodTwo', resOne, function(err, resTwo) {
if (err) {
return reject(err);
}
fulfill(resTwo);
});
});
}).then(function(resTwo) {
return new RSVP.Promise(function(fulfill, reject) {
Meteor.call('methodTwo', resTwo, function(err, resThree) {
if (err) {
reject(err);
}
fulfill(resThree);
});
});
}).then(function(resThree) {
// resThree is available - continue as you like
console.log(resThree);
}).catch(function(err) {
console.log(err);
});
That's the way to prevent "the ever rightward drift" of your code.
Promises are cool, use them.