I'm currently experimenting with promises and have a really basic question!
Within a promise chain, would it be bad practice to call a synchronous function? For example:
.then(function(results) {
if(checkIfResultInMemory(results) === true){
return getTotalFromMemory()
}
return results;
})
Or should my sync functions be refactored to return promises also?
Within a promise chain, would it be bad practice to call a synchronous
function?
No, it is not a bad practice at all. It is one of many expected and useful practices.
You are perfectly free to call either synchronous functions within the promise chain (from within .then() handlers) or asynchronous functions that then return a new promise.
When you return something from a .then() handler, you can return either a value (which becomes the resolved value of the parent promise) or you can return another promise (which chains onto the previous promise) or you can throw which works like returning a rejected promise (the promise chain becomes rejected).
So, that means you can call a synchronous function and get a value from it or call an async function and get another promise and then return either from the .then() handler.
All of these synchronous things are perfectly legal and each have their own objective. Here are some synchronous happenings in the .then() handler:
// modify resolved value
someAsync().then(function(val) {
return val + 12;
});
// modify resolved value by calling some synchronous function to process it
someAsync().then(function(val) {
return someSynchronousFunction(val);
});
// synchronously check the value and throw to change the promise chain
// to rejected
someAsync().then(function(val) {
if (val < 0) {
throw new Error("value can't be less than zero");
}
return val;
});
// synchronously check the value and return a rejected promise
// to change the promise chain to rejected
someAsync().then(function(val) {
if (val < 0) {
return Promise.reject("value can't be less than zero");
}
return val;
});
Here's a little example of an async operation that returns a promise followed by three synchronous .then() handlers and then outputting the final value:
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
function increment5(val) {
return val + 5;
}
delay(500, 10).then(increment5).then(function(val) {
return val - 3;
}).then(function(final) {
document.write(final);
});
Note: You only generally only want to use promises when you have or may have asynchronous operations because if everything is synchronous, then pure synchronous code is both faster to execute and easier to write. But, if you already have at least one async operation, you can certainly mix synchronous operations with that async operation and use promises to help structure the code.
A then callback function should:
return another promise
return a synchronous value (or undefined)
throw a synchronous error
Related
Assume an Express route that makes a call to Mongoose and has to be async so it can await on the mongoose.find(). Also assume we are receiving XML but we have to change it to JSON, and that also needs to be async so I can call await inside of it.
If I do this:
app.post('/ams', async (req, res) => {
try {
xml2js.parseString(xml, async (err, json) => {
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
} catch(err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " + err;
res.send(message);
}
}
}
The server hangs forever. I'm assuming the async/await means that the server hits a timeout before something concludes.
If I put this:
res.status(200);
res.send("Data saved")
on the line before the catch(), then that is returned, but it is the only thing every returned. The client gets a 200, even if an XMLException is thrown.
I can see the XMLException throw in the console, but I cannot get a 400 to send back. I cannot get anything I that catch block to execute in a way that communicates the response to the client.
Is there a way to do this?
In a nutshell, there is no way to propagate an error from the xml2js.parseString() callback up to the higher code because that parent function has already exited and returned. This is how plain callbacks work with asynchronous code.
To understand the problem here, you have to follow the code flow for xml2js.parseString() in your function. If you instrumented it like this:
app.post('/ams', async (req, res) => {
try {
console.log("1");
xml2js.parseString(xml, async (err, json) => {
console.log("2");
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
console.log("3");
} catch (err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " + err;
res.send(message);
}
}
console.log("4");
});
Then, you would see this in the logs:
1 // about to call xml2js.parseString()
3 // after the call to xml2js.parseString()
4 // function about to exit
2 // callback called last after function returned
The outer function has finished and returned BEFORE your callback has been called. This is because xml2js.parseString() is asynchronous and non-blocking. That means that calling it just initiates the operation and then it immediately returns and the rest of your function continues to execute. It works in the background and some time later, it posts an event to the Javascript event queue and when the interpreter is done with whatever else it was doing, it will pick up that event and call the callback.
The callback will get called with an almost empty call stack. So, you can't use traditional try/catch exceptions with these plain, asynchronous callbacks. Instead, you must either handle the error inside the callback or call some function from within the callback to handle the error for you.
When you try to throw inside that plain, asynchronous callback, the exception just goes back into the event handler that triggered the completion of the asynchronous operation and no further because there's nothing else on the call stack. Your try/catch you show in your code cannot catch that exception. In fact, no other code can catch that exception - only code within the exception.
This is not a great way to write code, but nodejs survived with it for many years (by not using throw in these circumstances). However, this is why promises were invented and when used with the newer language features async/await, they provide a cleaner way to do things.
And, fortunately in this circumstance xml2js.parseString() has a promise interface already.
So, you can do this:
app.post('/ams', async (req, res) => {
try {
// get the xml data from somewhere
const json = await xml2js.parseString(xml);
// do something with json here
res.send("Data saved");
} catch (err) {
console.log(err);
res.status(400).send("Malformed XML error: " + err.message);
}
});
With the xml2js.parseString() interface, if you do NOT pass it a callback, it will return a promise instead that resolves to the final value or rejects with an error. This is not something all asynchronous interfaces can do, but is fairly common these days if the interface had the older style callback originally and then they want to now support promises. Newer interfaces are generally just built with only promise-based interfaces. Anyway, per the doc, this interface will return a promise if you don't pass a callback.
You can then use await with that promise that the function returns. If the promise resolves, the await will retrieve the resolved value of the promise. If the promise rejects, because you awaiting the rejection will be caught by the try/catch. FYI, you can also use .then() and .catch() with the promise, but in many cases, async and await are simpler so that's what I've shown here.
So, in this code, if there is invalid XML, then the promise that xml2js.parseString() returns will reject and control flow will go to the catch block where you can handle the error.
If you want to capture only the xml2js.parseString() error separately from other exceptions that could occur elsewhere in your code, you can put a try/catch around just it (though this code didn't show anything else that would likely throw an exception so I didn't add another try/catch). In fact, this form of try/catch can be used pretty much like you would normally use it with synchronous code. You can throw up to a higher level of try/catch too.
A few other notes, many people who first start programming with asynchronous operations try to just put await in front of anything asynchronous and hope that it solves their problem. await only does anything useful when you await a promise so your asynchronous function must return a promise that resolves/rejects when the asynchronous operation is complete for the await to do anything useful.
It is also possible to take a plain callback asynchronous function that does not have a promise interface and wrap a promise interface around it. You pretty much never want to mix promise interface functions with plain callback asynchronous operations because error handling and propagation is a nightmare with a mixed model. So, sometimes you have to "promisify" an older interface so you can use promises with it. In most cases, you can do that with util.promisify() built into the util library in nodejs. Fortunately, since promises and async/await are the modern and easier way to do asynchronous things, most newer asynchronous interfaces in the nodejs world come with promise interfaces already.
You are throwing exceptions inside the callback function. So you cant expect the catch block of the router to receive it.
One way to handle this is by using util.promisify.
try{
const util = require('util');
const parseString = util.promisify(xml2js.parseString);
let json = await parsestring(xml);
}catch(err)
{
...
}
I'm struggling a bit with async/await and returning a value from a Promise.
function test () {
return new Promise((resolve, reject) => {
resolve('Hello')
})
}
async function c() {
await test()
}
As I understood things I should be able to get a value by doing:
console.log(c())
But clearly I am missing a point here as this returns a promise. Shouldn't it print "hello"? On a similar note I am unclear as to whether a callback needs to be converted to a promise before wrapping it in async/await?
I am missing a point here as this returns a promise. Shouldn't console.log(c()) print "hello"?
No, async functions always return promises. They're not magically running asynchronous code synchronously - rather the reverse, they turn synchronous-looking code (albeit speckled with await keywords) into asynchronously running one.
You can get the result value inside the asynchronous function:
async function c() {
const result = await test()
console.log(result);
return 'World';
}
c().then(console.log);
I am unclear as to whether a callback needs to be converted to a promise before wrapping it in async/await?
Yes, you can await only promises. See How do I convert an existing callback API to promises? for how to do the conversion.
Async functions return a Promise. If the function throws an error, the
Promise will be rejected. If the function returns a value, the Promise
will be resolved.
I'm writing a JavaScript function that makes an HTTP request and returns a promise for the result (but this question applies equally for a callback-based implementation).
If I know immediately that the arguments supplied for the function are invalid, should the function throw synchronously, or should it return a rejected promise (or, if you prefer, invoke callback with an Error instance)?
How important is it that an async function should always behave in an async manner, particularly for error conditions? Is it OK to throw if you know that the program is not in a suitable state for the async operation to proceed?
e.g:
function getUserById(userId, cb) {
if (userId !== parseInt(userId)) {
throw new Error('userId is not valid')
}
// make async call
}
// OR...
function getUserById(userId, cb) {
if (userId !== parseInt(userId)) {
return cb(new Error('userId is not valid'))
}
// make async call
}
Ultimately the decision to synchronously throw or not is up to you, and you will likely find people who argue either side. The important thing is to document the behavior and maintain consistency in the behavior.
My opinion on the matter is that your second option - passing the error into the callback - seems more elegant. Otherwise you end up with code that looks like this:
try {
getUserById(7, function (response) {
if (response.isSuccess) {
//Success case
} else {
//Failure case
}
});
} catch (error) {
//Other failure case
}
The control flow here is slightly confusing.
It seems like it would be better to have a single if / else if / else structure in the callback and forgo the surrounding try / catch.
This is largely a matter of opinion. Whatever you do, do it consistently, and document it clearly.
One objective piece of information I can give you is that this was the subject of much discussion in the design of JavaScript's async functions, which as you may know implicitly return promises for their work. You may also know that the part of an async function prior to the first await or return is synchronous; it only becomes asynchronous at the point it awaits or returns.
TC39 decided in the end that even errors thrown in the synchronous part of an async function should reject its promise rather than raising a synchronous error. For example:
async function someAsyncStuff() {
return 21;
}
async function example() {
console.log("synchronous part of function");
throw new Error("failed");
const x = await someAsyncStuff();
return x * 2;
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
There you can see that even though throw new Error("failed") is in the synchronous part of the function, it rejects the promise rather than raising a synchronous error.
That's true even for things that happen before the first statement in the function body, such as determining the default value for a missing function parameter:
async function someAsyncStuff() {
return 21;
}
async function example(p = blah()) {
console.log("synchronous part of function");
throw new Error("failed");
const x = await Promise.resolve(42);
return x;
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
That fails because it tries to call blah, which doesn't exist, when it runs the code to get the default value for the p parameter I didn't supply in the call. As you can see, even that rejects the promise rather than throwing a synchronous error.
TC39 could have gone the other way, and had the synchronous part raise a synchronous error, like this non-async function does:
async function someAsyncStuff() {
return 21;
}
function example() {
console.log("synchronous part of function");
throw new Error("failed");
return someAsyncStuff().then(x => x * 2);
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
But they decided, after discussion, on consistent promise rejection instead.
So that's one concrete piece of information to consider in your decision about how you should handle this in your own non-async functions that do asynchronous work.
How important is it that an async function should always behave in an async manner, particularly for error conditions?
Very important.
Is it OK to throw if you know that the program is not in a suitable state for the async operation to proceed?
Yes, I personally think it is OK when that is a very different error from any asynchronously produced ones, and needs to be handled separately anyway.
If some userids are known to be invalid because they're not numeric, and some are will be rejected on the server (eg because they're already taken) you should consistently make an (async!) callback for both cases. If the async errors would only arise from network problems etc, you might signal them differently.
You always may throw when an "unexpected" error arises. If you demand valid userids, you might throw on invalid ones. If you want to anticipate invalid ones and expect the caller to handle them, you should use a "unified" error route which would be the callback/rejected promise for an async function.
And to repeat #Timothy: You should always document the behavior and maintain consistency in the behavior.
Callback APIs ideally shouldn't throw but they do throw because it's very hard to avoid since you have to have try catch literally everywhere. Remember that throwing error explicitly by throw is not required for a function to throw. Another thing that adds to this is that the user callback can easily throw too, for example calling JSON.parse without try catch.
So this is what the code would look like that behaves according to these ideals:
readFile("file.json", function(err, val) {
if (err) {
console.error("unable to read file");
}
else {
try {
val = JSON.parse(val);
console.log(val.success);
}
catch(e) {
console.error("invalid json in file");
}
}
});
Having to use 2 different error handling mechanisms is really inconvenient, so if you don't want your program to be a fragile house of cards (by not writing any try catch ever) you should use promises which unify all exception handling under a single mechanism:
readFile("file.json").then(JSON.parse).then(function(val) {
console.log(val.success);
})
.catch(SyntaxError, function(e) {
console.error("invalid json in file");
})
.catch(function(e){
console.error("unable to read file")
})
Ideally you would have a multi-layer architecture like controllers, services, etc. If you do validations in services, throw immediately and have a catch block in your controller to catch the error format it and send an appropriate http error code. This way you can centralize all bad request handling logic. If you handle each case youll end up writing more code. But thats just how I would do it. Depends on your use case
I have written code using Q.reduce mechanism where function insertItemIntoDatabase(item) returns resolved promises.
items.reduce(function(soFar,item)
{
return soFar.then(function()
{
return insertItemIntoDatabase(item);
});
},Q());
Is there any possibilty to wait till chain is finished and then execute another function or I should chain those functions in some other way.
Thanks for help in advance
.reduce() will return the final value from the .reduce() loop, which in your case is a final promise. To execute something after the .reduce() chain and all its async operations are done, you just put a .then() handler on that final promise that is returned:
items.reduce(function(soFar,item) {
return soFar.then(function() {
return insertItemIntoDatabase(item);
});
},Q()).then(someOtherFunction);
The purpose of this pattern is exactly that: to return a promise for the result of the sequence. Written out (without reduce), it's exactly equivalent to the expression
Q()
.then(function() { return insertItemIntoDatabase(items[0]); })
.then(function() { return insertItemIntoDatabase(items[1]); })
.then(function() { return insertItemIntoDatabase(items[2]); })
…
You can simple add another .then() on it that will call your callbacks at the end of the chain.
function saveToTheDb(value) {
return new Promise(function(resolve, reject) {
db.values.insert(value, function(err, user) { // remember error first ;)
if (err) {
return reject(err); // don't forget to return here
}
resolve(user);
})
}
}
Here is the code which i see from here.
i am confused about return keyword.
For resolve(user);, do i need return?
For reject(user);, do i need return?
There is no need to use a return statement inside a new Promise() callback. The Promise constructor is not expecting any sort of return value from the callback.
So, the reason to use a return statement inside that callback is only to control the flow of execution in that function. If you want execution inside your callback to finish and not execute any more code within that callback, you can issue a return; at that point.
For example, you could have written your code like this with no return statement:
function saveToTheDb(value) {
return new Promise(function(resolve, reject) {
db.values.insert(value, function(err, user) {
if (err) {
reject(err);
} else {
resolve(user);
}
});
}
}
In this case, you used the if/else clause to make sure the flow of control in your function takes the correct path and no return was needed or used.
A common shortcut when promisifying async functions like this is:
function saveToTheDb(value) {
return new Promise(function(resolve, reject) {
db.values.insert(value, function(err, user) {
if (err) return reject(err);
resolve(user);
});
}
}
This is not functionally different than the previous code block, but it is less typing and more compact. The return statement in front of reject(err); is only for flow of control reasons to prevent from executing the resolve(user); statement in case of error since the desired flow of control is to call reject(err) and then not execute anything else in the callback.
In fact, the return statement in this last block is not actually even needed in this specific case because executing a resolve() after a reject() will not do anything since promises are latched to whichever happens first resolve or reject. But, it is generally considered poor practice to execute unnecessary code so many would argue that it is better to use flow of control structures such as if/else or return to only execute the code that is needed.
So, this would technically work too, but is not considered a best practice because it executes unnecessary code and isn't as clearly structured:
function saveToTheDb(value) {
return new Promise(function(resolve, reject) {
db.values.insert(value, function(err, user) {
if (err) reject(err);
resolve(user);
});
}
}
FYI, what you are doing here is called "promisifying" which makes a regular async function that works with a callback into a function that returns a promise. There are libraries and functions that will "promisify" a function or a whole object of functions (e.g. a whole API) for you in one function call so you don't have to do this manually. For example, I regularly use Bluebird which offers Promise.promisify() for promisifying a single function or Promise.promisifyAll() which will promisify all the methods on an object or prototype. This is very useful. For example, you could get promisified versions of the entire fs module with just this:
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
Then, you can use methods that return a promise such as:
fs.readFileAsync("file.txt").then(function(data) {
// do something with file.txt data here
});
Generally, in NodeJS, you shouldn't use the promise constructor very much.
The promise constructor is for converting APIs that don't return promises to promises. You should consider using a library that provides promisification (even if you use native promises all-around) since it provides a safe alternative that does not have subtle errors with error-handling logic.
Automatic promisification is also considerably faster.
That said, the answer to your question is "Yes".
It is perfectly safe to do so, there is nothing special about promise constructors - they are just plain JavaScript. Domenic discusses the design of the promise constructor in his blog.
It is perfectly safe (just like any other function) to return early - it is actually quite common in regular asynchronous functions.
(Also, in your example code you should just use Promise.resolve, but I assume it was that simple only because it is an example).
Copied this answer from duplicate
As #JaromandaX said in this case the return statement does not make any diference.
From the docs:
In all cases where a promise is resolved (i.e. either fulfilled or rejected), the resolution is permanent and cannot be reset. Attempting to call resolve, reject, or notify if promise is already resolved will be a no-op.