I'm having difficulty finding an answer to my question, perhaps because I don't know how to ask it (what search terms to use). I'm really struggling to understand promises, and have watched a number of tutorial videos and am still not getting some fundamental piece to make it click.
In Node, I am using the request-promise module, which returns a promise when I make a call to an rp() method. I am calling
return rp(service);
from within a function. But what I want to do, instead, is add a .then() handler to this to do some post-processing with the result of the service call, BEFORE returning the promise back to the caller so the caller can still have its own then handler.
How do I accomplish this?
It would be helpful to see your current code to better understand where exactly you are struggling with.
In general, you can think of promises as a placeholder for some future value that you can chain together.
If I understood your question correctly, you would like to make a request to a server, receive the response, do something with the response and return it back to the caller.
const callService = (url) => {
// make sure to return your promise here
return rp(url)
.then((html) => {
// do something with html
return html;
});
};
Now you can call it like so -
callService('http://www.google.com')
.then((html) => {
console.log(html);
}).catch((err) => {
// error handling
});
The common mistake is to omit the return statement in your top level function and subsequent then functions.
Feel free to provide additional details.
Related
I'm struggling with callbacks in Node.js. I simply want playerNumber to be set to the number of players in my collection of Players. The console.log works, but I can't get the variable out of the function and into the playerNumber variable.
And if there's a simpler way get this value for use in the rest of my backend code, I'm all ears. I'm clearly new at Node.js, but the code always seems more involved than I'm expecting.
Thanks in advance!
var playerNumber = function countPlayers(callback){
Player.count(function(err, numOfDocs) {
console.log('I have '+numOfDocs+' documents in my collection');
callback(err, numOfDocs);
});
}
It's probably async, and it's a typical first-timer experience to want to "get back to normal" on the call chain on the way back from async call. This can't be done, but it's not so bad to live with it. Here's how...
Step 1: Promises are better than callbacks. I'll leave the long story
to others.
Step 2: Callbacks can be made into promises
In the OP case...
// The promise constructor takes a function that has two functions as params
// one to call on success, and one to call on error. Instead of a callback
// call the 'resolve' param with the data and the 'reject' param with any error
// mark the function 'async' so callers know it can be 'await'-ed
const playerNumber = async function countPlayers() {
return new Promise((resolve, reject) => {
Player.count(function(err, numOfDocs) {
err ? reject(err) : resolve(numOfDocs);
});
});
}
Step 3: Yes, the callers must deal with this, and the callers of the callers, and so on. It's not so bad.
In the OP case (in the most modern syntax)...
// this has to be async because it 'awaits' the first function
// think of await as stopping serial execution until the async function finishes
// (it's not that at all, but that's an okay starting simplification)
async function printPlayerCount() {
const count = await playerNumber();
console.log(count);
}
// as long as we're calling something async (something that must be awaited)
// we mark the function as async
async function printPlayerCountAndPrintSomethingElse() {
await printPlayerCount();
console.log('do something else');
}
Step 4: Enjoy it, and do some further study. It's actually great that we can do such a complex thing so simply. Here's good reading to start with: MDN on Promises.
When I hover on the keyword 'function' the description says:
"(local function)(this: any, next: (err?: mongoose.CallbackError | undefined) => void): Promise<void>"
So does It return a Promise<void> or a simple <void>? I can't even understand what does this function returns? And to be honest I don't understand really well the concept of Promise<void>...
userSchema.pre('save', async function (next) {
let user = this as UserDocument;
if(!user.isModified('password')){
return next();
}
const salt = await bcrypt.genSalt(config.get<number>('saltWorkFactor'));
const hash = await bcrypt.hash(user.password, salt);
user.password = hash;
return next();
})
This question is really interesting. Your function returns a Promise<void>, which is compatible with the void return type that pre is expecting, but Mongoose is quietly smart enough to know what to do with your Promise so you don't even have to call next at all.
First some background:
void has a special meaning in TypeScript to mean that the return value could be any value; the value is frequently undefined (because that's what a function returns without a return statement) but it doesn't have to be. As in the TypeScript FAQ, this makes it convenient to accept or pass functions that return a value by indicating the return value is unused. If you need to supply a function with return type void, you could pass back a function that returns a string, Promise<void>, Promise<SomeObject>, null, undefined, or anything else.
All async functions return Promises, and this is no exception. A Promise<number> is a Promise that says that its then function will receive a number; a Promise<void> is a Promise that doesn't tell you anything about what its then function receives. The then function will still be called, unless it has an error to catch; you just don't know much about its argument.
In Mongoose's types, pre takes a PreSaveMiddlewareFunction<T> function, which is the type of the function you wrote. It accepts a function called next and returns void: Mongoose claims not to care what you return. Your middleware function is allowed to be asynchronous; when you're done you're expected to call next (with an error object, if you have one), and that call to next also returns void.
Your function passed to pre returns type Promise<void>: The function is async so it absolutely returns a promise, and your return next(); means that the Promise resolves to whatever next returns, which is defined as void. You don't know what next returns and shouldn't care about it. You don't even need to return next(), you just need to call it: It's just a callback so you can tell Mongoose your middleware is done and report any errors.
So your async function returns Promise<void>, but that works with the definition of pre: pre doesn't care what kind of return value your function has (void) as long as you call next to indicate you're done.
But wait! Reporting that your asynchronous function is done and whether or not there were errors is exactly the problem that Promises were designed to solve, and the next callback pattern is exactly the kind of pattern that Promises were designed to replace. If you're returning a Promise, why would you need to call next at all when Mongoose can just watch the promise you return?
In fact, in Mongoose 5.x or later, that's exactly what happens: If the function you pass into pre returns a Promise, then you can use that instead of calling next. You can still call next manually for compatibility's sake, but in your case you could delete return next() and everything would keep working. See the middleware docs:
In mongoose 5.x, instead of calling next() manually, you can use a function that returns a promise. In particular, you can use async/await.
schema.pre('save', function() {
return doStuff().
then(() => doMoreStuff());
});
// Or, in Node.js >= 7.6.0:
schema.pre('save', async function() {
await doStuff();
await doMoreStuff();
});
The docs further explain why return next() is a pattern at all:
If you use next(), the next() call does not stop the rest of the code in your middleware function from executing. Use the early return pattern to prevent the rest of your middleware function from running when you call next().
const schema = new Schema(..);
schema.pre('save', function(next) {
if (foo()) {
console.log('calling next!');
// `return next();` will make sure the rest of this function doesn't run
/*return*/ next();
}
// Unless you comment out the `return` above, 'after next' will print
console.log('after next');
});
In summary, the expected return type of void is compatible with the fact that you're returning a Promise<void>, but it hides the fact that recent versions of Mongoose are smart enough to check whether you're returning a Promise and do the right thing without needing a call to next. They're two different styles that both work.
Long answer short: It return a Promise<void>
Callbacks
To understand why, here are some details.
First one must understand Callbacks in node.js. Callbacks are one of the basic structure/feature of how node.js works.
You could say that node.js is basically an Event-Driven Programming "framework" (most people will frown to the framework word...). That means that you tell node that in the event of a certain thing happening, it should do a certain action/function (callback).
For node to understand us, we normally give the callback function as a parameter to another function that will do the work of "listening to the event" and executing the callback that we give it. So it is not "us" that execute the callback, it is the event listener.
In your case,
userSchema.pre('save', async function (next) {
pre is the function (a method in Mongoose's userSchema), save is the event that one must react to, async function (next) { is the callback or what must be done after the event.
You will note that your callback is returning next(), but next() returns void, which mean that your callback is returning void.
So why is it returning Promise<void>?
The fact is that in your case, your callback is an async function. And every async functions will return a promise. It is an async function because it is awaiting another promise (two promises even) inside of it. They are hidden because of the await
const salt = await bcrypt.genSalt(config.get<number>('saltWorkFactor'));
const hash = await bcrypt.hash(user.password, salt);
Note: The bcrypt methods are very expensive in terms of CPU and time (also a security feature among other things).
It also means that normally in your code
const hash = await bcrypt.hash(user.password, salt);
user.password = hash;
you couldn't have available "right away" the hash value for the user.password and, worse, you couldn't even know when it would come. Will your program stop and wait until bcrypt finish its business?
If you have many async functions, your program will be a great favourite for the slowest champion in the Olympics.
What is going on with those promises and how can we not be labelled as a geriatric program?
Promises
Here is a quick/long comment to try to explain the concept of promises.
In "normal" code, each lines of code is executed and "finished" before the next one. Ex: (with cooking)
Combine the Butter and Sugar,
Add Eggs One at a Time, etc.
Or in your code:
let user = this as UserDocument;
if(!user.isModified('password')){
return next();
}
A promise is a certain code that is executed but not finished before the next line of code. Ex:
while the cake is in the oven (promise),
you prepare the frosting,
but you can't put it until the cake in baked (the "then" action of promises).
Note: Your code is using await so there is no "explicit" then method.
You will have many example of "promises" things in everyday life. you may have heard of asynchronous code = not one after the other, not in sync, ...
Turning on an alarm to wake you in the morning, then you make the promise that you will not ignore it;
putting a reminder on the calendar then you make the promise that you will go to that job interview; etc.
All the while, you continue with your life after making those promises.
In code, a function that returns a promise will have a then method where you tell the computer what to do when when the "alarms goes off".
It is usually written like this
mypromise().then(doThisThingFunction)
const continueWithMyLife = true
In this way the then method is very similar to the callback of node.js. It is just expressed in a different way in the code and is not specific to node (callbacks are also not specific to node...).
One very important difference between them is that callbacks are something that the listener "do" and promises is something that resolves (hopefully) to a returning value.
Async/Await
Nowadays it is common to use async/await. Fortunately/unfortunately it basically hides the asynchronous behaviour. Better flow of reading the code, but also much worse understanding of promises for new programmers.
After a await, there is no then method (Or you could say that the following line of code is the then action). There is no "continuing with your life". There is only "waiting until the alarms goes off", So the next line after the await is essentially the "get out of the bed action".
That is why, in your code, the hash value is available in the next line. Basically in the "old way" to write promises
user.password = hash;
would be inside the then function.
And that is also why it is returning Promise<void>
But still, all these analogies won't really help. The best is to try it in everyday code. There is nothing like experience to understand anything.
I am using https://github.com/request/request.
The example that is given is:
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
How can I use body elsewhere in my code? I want something like return body but nothing works. I can't use it anywhere!
You cannot directly return an asynchronous result from outside the request() callback. This is not unique to this particular function, but is how all asynchronous callbacks work in Javascript.
When you call request() it starts an asynchronous operation and turns it over to native code. The JS interpreter then goes about it's merry way executing the rest of your Javscript after this function call (not in the callback, but after that). So, you should be able to immediately see that the body result does not exist yet, but the rest of your Javascript is already executing.
Then, some indeterminate time later (depending upon how responsive the server is that you're contacting and how big the result is), when the JS interpreter has nothing else to do, the callback gets called and the body result is available.
So, the ONLY place in your code that you know the body result is good and is available is INSIDE that callback. So, the general way of programming with an asynchronous operation like this in Javascript is that you use the result inside the callback. Any code that needs to use that result gets put inside that callback or you can put the code in a separate function and call that function from inside the callback and pass the body result as an argument to the function.
If you wanted to wrap this request in a function and communicate back that result to the caller (which is not exactly what you show here, but another way to write the code), I'd suggest you first read How do I return the response from an asynchronous call? because it outlines the various ways you can communicate back the body result from your asynchronous operation (another callback or use promises).
To many newbie Javascript developers this seems somewhat heretical. What do you mean I can't just call a function, get the result and return it from the function? Well, that's the extra complication with Javascripts non-blocking, asynchronous I/O model that is entirely event driven. Once you get down the learning curve, you will find a huge number of advantages in the language that flow from this architectural model, but you will have to deal with this extra complication.
The language is evolving to make this type of programming simpler with the use of promises in async functions and then the use of await to "wait" on a promise. If you use the request-promise library instead of the request library (request-promise is derived from request), then you can deal with a promise as the return value and you have more options.
const rp = require('request-promise');
async getMeSomeData() {
try {
let body = await rp('http://www.google.com');
console.log(body);
// can put code here that uses body
return body; // becomes the resolved value of the returned promise
} catch(err) {
console.log(err);
throw err; // makes sure the returned promise is rejected
}
});
getMeSomeData().then(body => {
// use the body here
}).catch(err => {
// error here
});
Note: I showed possibly using the body value or err inside getMeSomeData() or at the caller. Usually, you would do one or the other, but I wanted to show both ways.
I am new to nodejs/expressjs and mongodb. I am trying to create an API that exposes data to my mobile app that I am trying to build using Ionic framework.
I have a route setup like this
router.get('/api/jobs', (req, res) => {
JobModel.getAllJobsAsync().then((jobs) => res.json(jobs)); //IS THIS THe CORRECT WAY?
});
I have a function in my model that reads data from Mongodb. I am using the Bluebird promise library to convert my model functions to return promises.
const JobModel = Promise.promisifyAll(require('../models/Job'));
My function in the model
static getAllJobs(cb) {
MongoClient.connectAsync(utils.getConnectionString()).then((db) => {
const jobs = db.collection('jobs');
jobs.find().toArray((err, jobs) => {
if(err) {
return cb(err);
}
return cb(null, jobs);
});
});
}
The promisifyAll(myModule) converts this function to return a promise.
What I am not sure is,
If this is the correct approach for returning data to the route callback function from my model?
Is this efficient?
Using promisifyAll is slow? Since it loops through all functions in the module and creates a copy of the function with Async as suffix that now returns a promise. When does it actually run? This is a more generic question related to node require statements. See next point.
When do all require statements run? When I start the nodejs server? Or when I make a call to the api?
Your basic structure is more-or-less correct, although your use of Promise.promisifyAll seems awkward to me. The basic issue for me (and it's not really a problem - your code looks like it will work) is that you're mixing and matching promise-based and callback-based asynchronous code. Which, as I said, should still work, but I would prefer to stick to one as much as possible.
If your model class is your code (and not some library written by someone else), you could easily rewrite it to use promises directly, instead of writing it for callbacks and then using Promise.promisifyAll to wrap it.
Here's how I would approach the getAllJobs method:
static getAllJobs() {
// connect to the Mongo server
return MongoClient.connectAsync(utils.getConnectionString())
// ...then do something with the collection
.then((db) => {
// get the collection of jobs
const jobs = db.collection('jobs');
// I'm not that familiar with Mongo - I'm going to assume that
// the call to `jobs.find().toArray()` is asynchronous and only
// available in the "callback flavored" form.
// returning a new Promise here (in the `then` block) allows you
// to add the results of the asynchronous call to the chain of
// `then` handlers. The promise will be resolved (or rejected)
// when the results of the `job().find().toArray()` method are
// known
return new Promise((resolve, reject) => {
jobs.find().toArray((err, jobs) => {
if(err) {
reject(err);
}
resolve(jobs);
});
});
});
}
This version of getAllJobs returns a promise which you can chain then and catch handlers to. For example:
JobModel.getAllJobs()
.then((jobs) => {
// this is the object passed into the `resolve` call in the callback
// above. Do something interesting with it, like
res.json(jobs);
})
.catch((err) => {
// this is the error passed into the call to `reject` above
});
Admittedly, this is very similar to the code you have above. The only difference is that I dispensed with the use of Promise.promisifyAll - if you're writing the code yourself & you want to use promises, then do it yourself.
One important note: it's a good idea to include a catch handler. If you don't, your error will be swallowed up and disappear, and you'll be left wondering why your code is not working. Even if you don't think you'll need it, just write a catch handler that dumps it to console.log. You'll be glad you did!
I'm new to node.js and using it for a backend that takes data from syslog messages and stores it to a database.
I've run into the following type of serial operations:
1. Query the database
2. If the query value is X do A. otherwise do B.
A. 1. Store "this" in DB
2. Query the database again
3. If the query value is Y do P. otherwise do Q.
P. Store "something"
Q. Store "something else"
B. 1. Store "that" in the DB
2. Store "the other thing" in the DB
The gist here is that I have some operations that need to happen in order, but there is branching logic in the order.
I end up in callback hell (I didn't know what that is when I came to Node.. I do now).
I have used the async library for things that are more straight forward - like doing things in order with async.forEachOfSeries or async.queue. But I don't think there's a way to use that if things have to happen in order but there is branching.
Is there a way to handle this that doesn't lead to callback hell?
Any sort of complicated logic like this is really, really going to benefit from using promises for every async operation, especially when you get to handling errors, but also just for structuring the logic flow.
Since you haven't provided any actual code, I will make up an example. Suppose you have two core async operations that both return a promise: query(...) and store(...).
Then, you could implement your above logic like this:
query(...).then(function(value) {
if (value === X) {
return store(value).then(function() {
return query(...).then(function(newValue) {
if (newValue === Y) {
return store("something");
} else {
return store("something else");
}
})
});
} else {
return store("that").then(function() {
return store("the other thing");
});
}
}).then(function() {
// everything succeeded here
}, function(err) {
// error occurred in anyone of the async operations
});
I won't pretend this is simple. Implemented logic flow among seven different async operation is just going to be a bit of code no matter how you do it. But, promises can make it less painful than otherwise and massively easier to make robust error handling work and to interface this with other async operations.
The main keys of promises used here are:
Make all your async operations returning promises that resolve with the value or reject with the failure as the reason.
Then, you can attach a .then() handler to any promise to see when the async operation is finished successfully or with error.
The first callback to .then() is the success handler, the second callback is the error handler.
If you return a promise from within a .then() handler, then that promise gets "chained" to the previous one so all success and error state becomes linked and gets returned back to the original promise state. That's why you see the above code always returning the nested promises. This automates the returning of errors all the way back to the caller and allows you to return a value all the way back to the caller, even from deeply nested promises.
If any promise in the above code rejects, then it will stop that chain of promises up until an actual reject handler is found and propagate that error back to that reject handler. Since the only reject handler in the above code is at the to level, then all errors can be caught and seen there, no matter how deep into the nested async mess it occurred (try doing that reliably with plain callbacks - it's quite difficult).
Native Promises
Node.js has promises built in now so you can use native promises for this. But, the promise specification that node.js implements is not particularly feature rich so many folks use a promise library (I use Bluebird for all my node.js development) to get additional features. One of the more prominent features is the ability to "promisify" an existing non-promise API. This allows you to take a function that works only with a callback and create a new function that works via a promise. I find this particularly useful since many APIs that have been around awhile do not natively return promises.
Promisifying a Non-Promise Interface
Suppose you had an async operation that used a traditional callback and you wanted to "promisify it". Here's an example of how you could do that manually. Suppose you had db.query(whatToSearchFor, callback). You can manually promisify it like this:
function queryAsync(whatToSearchFor) {
return new Promise(function(resolve, reject) {
db.query(whatToSearchFor, function(err, data) {
if (!err) {
reject(err);
} else {
resolve(data);
}
});
});
}
Then, you can just call queryAsync(whatToSearchFor) and use the returned promise.
queryAsync("foo").then(function(data) {
// data here
}, function(err) {
// error here
});
Or, if you use something like the Bluebird promise library, it has a single function for promisifying any async function that communicates its result via a node.js-style callback passed as the last argument:
var queryAsync = Promise.promisify(db.query, db);